diff --git a/README.md b/README.md index 0e47aae07..1dd1f4d16 100644 --- a/README.md +++ b/README.md @@ -150,8 +150,8 @@ You will find the application logs in `~/Library/Application Support/Windscribe/ ### Logs -- Client app and location pings: `/Users//Library/Application Support/Windscribe/Windsrcibe2` -- Installer: `/Users//Library/Application Support/Windscribe/Windsrcibe/log_installer.txt` +- Client app and location pings: `/Users//Library/Application Support/Windscribe/Windscribe2` +- Installer: `/Users//Library/Application Support/Windscribe/Windscribe/log_installer.txt` - Helper: `/Library/Logs/com.windscribe.helper.macos/helper_log.txt` ## Linux diff --git a/backend/linux/helper/server.cpp b/backend/linux/helper/server.cpp index bb65857b4..e3edbdcaf 100644 --- a/backend/linux/helper/server.cpp +++ b/backend/linux/helper/server.cpp @@ -383,6 +383,9 @@ bool Server::readAndHandleCommand(socket_ptr sock, boost::asio::streambuf *buf, conf << "client = yes\n"; conf << "accept = 127.0.0.1:" << cmd.localPort << "\n"; conf << "connect = " << cmd.hostname << ":" << cmd.port << "\n"; + if (cmd.extraPadding) { + conf << "options = TLSEXT_PADDING\noptions = TLSEXT_PADDING_SUPER\n"; + } int fd = open("/etc/windscribe/stunnel.conf", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU); if (fd < 0) { diff --git a/backend/mac/helper/server.cpp b/backend/mac/helper/server.cpp index 6934bab37..4bbf91298 100644 --- a/backend/mac/helper/server.cpp +++ b/backend/mac/helper/server.cpp @@ -470,6 +470,9 @@ bool Server::readAndHandleCommand(socket_ptr sock, boost::asio::streambuf *buf, conf << "client = yes\n"; conf << "accept = 127.0.0.1:" << cmd.localPort << "\n"; conf << "connect = " << cmd.hostname << ":" << cmd.port << "\n"; + if (cmd.extraPadding) { + conf << "options = TLSEXT_PADDING\noptions = TLSEXT_PADDING_SUPER\n"; + } int fd = open("/etc/windscribe/stunnel.conf", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU); if (fd < 0) { diff --git a/backend/posix_common/helper_commands.h b/backend/posix_common/helper_commands.h index 3d52b51f9..601f86c27 100644 --- a/backend/posix_common/helper_commands.h +++ b/backend/posix_common/helper_commands.h @@ -269,6 +269,7 @@ struct CMD_CONFIGURE_STUNNEL { std::string hostname; int port; int localPort; + bool extraPadding; }; struct CMD_START_WSTUNNEL { diff --git a/client/common/changelog.txt b/client/common/changelog.txt index f9c1076e6..bee7c8eec 100644 --- a/client/common/changelog.txt +++ b/client/common/changelog.txt @@ -1,3 +1,15 @@ +2.7.7 (29/06/2023) +All: + * Added anti-censorship feature: OpenVPN TCP split-reset, UDP stuffing, Stunnel extra padding. #671 + * Added anti-censorship feature: use extra TLS padding for fallback API requests. #678 + * Added Czech language translation. #677 + * Fixed Logout/Quit screen UI bugs. #457 + * Fixed Manage ROBERT rules link click does nothing. #480 + * Fixed Russian translation. #679 +Linux: + * Removed 'kill app' functionality from the uninstall script. #335 + + 2.7.6 (08/06/2023) All: * Fixed UI overlap and alignment issues. #457 & #583 diff --git a/client/common/utils/extraconfig.cpp b/client/common/utils/extraconfig.cpp index 28b91790a..7ce76beb1 100644 --- a/client/common/utils/extraconfig.cpp +++ b/client/common/utils/extraconfig.cpp @@ -26,6 +26,9 @@ const QString WS_WG_VERBOSE_LOGGING = WS_PREFIX + "wireguard-verbose-logging"; const QString WS_SCREEN_TRANSITION_HOTKEYS = WS_PREFIX + "screen-transition-hotkeys"; const QString WS_USE_ICMP_PINGS = WS_PREFIX + "use-icmp-pings"; +const QString WS_STEALTH_EXTRA_TLS_PADDING = WS_PREFIX + "stealth-extra-tls-padding"; +const QString WS_API_EXTRA_TLS_PADDING = WS_PREFIX + "api-extra-tls-padding"; + void ExtraConfig::writeConfig(const QString &cfg) { QMutexLocker locker(&mutex_); @@ -227,6 +230,16 @@ bool ExtraConfig::getUseICMPPings() return getFlagFromExtraConfigLines(WS_USE_ICMP_PINGS); } +bool ExtraConfig::getStealthExtraTLSPadding() +{ + return getFlagFromExtraConfigLines(WS_STEALTH_EXTRA_TLS_PADDING); +} + +bool ExtraConfig::getAPIExtraTLSPadding() +{ + return getFlagFromExtraConfigLines(WS_API_EXTRA_TLS_PADDING); +} + int ExtraConfig::getIntFromLineWithString(const QString &line, const QString &str, bool &success) { int endOfId = line.indexOf(str, Qt::CaseInsensitive) + str.length(); diff --git a/client/common/utils/extraconfig.h b/client/common/utils/extraconfig.h index c604f115b..d22d2fdb1 100644 --- a/client/common/utils/extraconfig.h +++ b/client/common/utils/extraconfig.h @@ -48,6 +48,8 @@ class ExtraConfig bool getWireGuardVerboseLogging(); bool getUsingScreenTransitionHotkeys(); bool getUseICMPPings(); + bool getStealthExtraTLSPadding(); + bool getAPIExtraTLSPadding(); private: ExtraConfig(); diff --git a/client/common/version/windscribe_version.h b/client/common/version/windscribe_version.h index 324f59b69..3eeaece65 100644 --- a/client/common/version/windscribe_version.h +++ b/client/common/version/windscribe_version.h @@ -2,7 +2,7 @@ #define WINDSCRIBE_MAJOR_VERSION 2 #define WINDSCRIBE_MINOR_VERSION 7 -#define WINDSCRIBE_BUILD_VERSION 6 +#define WINDSCRIBE_BUILD_VERSION 7 // only one of these should be enabled; neither -> stable //#define WINDSCRIBE_IS_BETA diff --git a/client/engine/engine/connectionmanager/stunnelmanager.cpp b/client/engine/engine/connectionmanager/stunnelmanager.cpp index ed2a42b44..4c2381f43 100644 --- a/client/engine/engine/connectionmanager/stunnelmanager.cpp +++ b/client/engine/engine/connectionmanager/stunnelmanager.cpp @@ -8,6 +8,7 @@ #endif #include "utils/executable_signature/executable_signature.h" #include "utils/logger.h" +#include "utils/extraconfig.h" StunnelManager::StunnelManager(QObject *parent, IHelper *helper) : QObject(parent), helper_(helper), bProcessStarted_(false), portForStunnel_(0) @@ -72,9 +73,10 @@ bool StunnelManager::setConfig(const QString &hostname, uint port) } #else portForStunnel_ = AvailablePort::getAvailablePort(DEFAULT_PORT); + bool extraPadding = ExtraConfig::instance().getStealthExtraTLSPadding(); Helper_posix *helper_posix = dynamic_cast(helper_); - return !helper_posix->configureStunnel(hostname, port, portForStunnel_); + return !helper_posix->configureStunnel(hostname, port, portForStunnel_ ,extraPadding); #endif } @@ -127,6 +129,10 @@ bool StunnelManager::makeConfigFile(const QString &hostname, uint port) file.write(str.toLocal8Bit()); str = "connect = " + hostname + ":" + QString::number(port) + "\r\n"; file.write(str.toLocal8Bit()); + if (ExtraConfig::instance().getStealthExtraTLSPadding()) { + str = "options = TLSEXT_PADDING\r\noptions = TLSEXT_PADDING_SUPER\r\n"; + file.write(str.toLocal8Bit()); + } file.close(); diff --git a/client/engine/engine/helper/helper_posix.cpp b/client/engine/engine/helper/helper_posix.cpp index 5b49eb6b7..57d3880b5 100644 --- a/client/engine/engine/helper/helper_posix.cpp +++ b/client/engine/engine/helper/helper_posix.cpp @@ -645,7 +645,7 @@ bool Helper_posix::startStunnel() return IHelper::EXECUTE_SUCCESS; } -bool Helper_posix::configureStunnel(const QString &hostname, unsigned int port, unsigned int localPort) +bool Helper_posix::configureStunnel(const QString &hostname, unsigned int port, unsigned int localPort, bool extraPadding) { QMutexLocker locker(&mutex_); @@ -653,6 +653,7 @@ bool Helper_posix::configureStunnel(const QString &hostname, unsigned int port, cmd.hostname = hostname.toStdString(); cmd.port = port; cmd.localPort = localPort; + cmd.extraPadding = extraPadding; std::stringstream stream; boost::archive::text_oarchive oa(stream, boost::archive::no_header); diff --git a/client/engine/engine/helper/helper_posix.h b/client/engine/engine/helper/helper_posix.h index eb11b6468..d5d7ee342 100644 --- a/client/engine/engine/helper/helper_posix.h +++ b/client/engine/engine/helper/helper_posix.h @@ -54,7 +54,7 @@ class Helper_posix : public IHelper bool getFirewallRules(CmdIpVersion version, const QString &table, const QString &group, QString &rules); bool setFirewallOnBoot(bool bEnabled, const QSet& ipTable); bool startStunnel(); - bool configureStunnel(const QString &hostname, unsigned int port, unsigned int localPort); + bool configureStunnel(const QString &hostname, unsigned int port, unsigned int localPort, bool extraPadding); bool startWstunnel(const QString &hostname, unsigned int port, bool isUdp, unsigned int localPort); protected: diff --git a/client/engine/engine/networkaccessmanager/curlnetworkmanager_impl.cpp b/client/engine/engine/networkaccessmanager/curlnetworkmanager_impl.cpp index 112c34a7b..c374904f7 100644 --- a/client/engine/engine/networkaccessmanager/curlnetworkmanager_impl.cpp +++ b/client/engine/engine/networkaccessmanager/curlnetworkmanager_impl.cpp @@ -93,6 +93,13 @@ bool CurlNetworkManagerImpl::setupBasicOptions(RequestInfo *requestInfo, const N return false; } + if (request.isExtraTLSPadding()) { +#ifdef CURLSSLOPT_TLSEXT_PADDING_SUPER + if (curl_easy_setopt(requestInfo->curlEasyHandle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_TLSEXT_PADDING | CURLSSLOPT_TLSEXT_PADDING_SUPER) != CURLE_OK) + return false; +#endif + } + curl_easy_setopt(requestInfo->curlEasyHandle, CURLOPT_PRIVATE, new quint64(requestInfo->id)); // our user data, must be deleted in the RequestInfo destructor return true; } diff --git a/client/engine/engine/networkaccessmanager/networkrequest.cpp b/client/engine/engine/networkaccessmanager/networkrequest.cpp index 4944ebf76..3bc5f1fef 100644 --- a/client/engine/engine/networkaccessmanager/networkrequest.cpp +++ b/client/engine/engine/networkaccessmanager/networkrequest.cpp @@ -1,7 +1,7 @@ #include "networkrequest.h" NetworkRequest::NetworkRequest(const QUrl &url, int timeout, bool bUseDnsCache) : url_(url), timeout_(timeout), bUseDnsCache_(bUseDnsCache), bIgnoreSslErrors_(false), - bRemoveFromWhitelistIpsAfterFinish_(false), isWhiteListIps_(true) + bRemoveFromWhitelistIpsAfterFinish_(false), isWhiteListIps_(true), bExtraTLSPadding_(false) { } @@ -12,7 +12,8 @@ NetworkRequest::NetworkRequest(const QUrl &url, int timeout, bool bUseDnsCache, bIgnoreSslErrors_(isIgnoreSslErrors), dnsServers_(dnsServers), bRemoveFromWhitelistIpsAfterFinish_(false), - isWhiteListIps_(true) + isWhiteListIps_(true), + bExtraTLSPadding_(false) { } @@ -96,6 +97,16 @@ QString NetworkRequest::echConfig() const return echConfig_; } +void NetworkRequest::setExtraTLSPadding(const bool ExtraTLSPadding) +{ + bExtraTLSPadding_ = ExtraTLSPadding; +} + +bool NetworkRequest::isExtraTLSPadding() const +{ + return bExtraTLSPadding_; +} + void NetworkRequest::setOverrideIp(const QString &ip) { overrideIp_ = ip; diff --git a/client/engine/engine/networkaccessmanager/networkrequest.h b/client/engine/engine/networkaccessmanager/networkrequest.h index b63e68ca5..b8ff6f6d2 100644 --- a/client/engine/engine/networkaccessmanager/networkrequest.h +++ b/client/engine/engine/networkaccessmanager/networkrequest.h @@ -33,6 +33,9 @@ class NetworkRequest void setEchConfig(const QString &echConfig); QString echConfig() const; + void setExtraTLSPadding(const bool ExtraTLSPadding); + bool isExtraTLSPadding() const; + // Explicitly specify ip to avoid DNS resolution void setOverrideIp(const QString &ip); QString overrideIp() const; @@ -58,5 +61,7 @@ class NetworkRequest // default true bool isWhiteListIps_; + + bool bExtraTLSPadding_; }; diff --git a/client/engine/engine/serverapi/requestexecuterviafailover.cpp b/client/engine/engine/serverapi/requestexecuterviafailover.cpp index 33c66b094..5c238f588 100644 --- a/client/engine/engine/serverapi/requestexecuterviafailover.cpp +++ b/client/engine/engine/serverapi/requestexecuterviafailover.cpp @@ -116,6 +116,9 @@ void RequestExecuterViaFailover::executeBaseRequest(const failover::FailoverData if (!failoverData.echConfig().isEmpty()) { networkRequest.setEchConfig(failoverData.echConfig()); } + if (ExtraConfig::instance().getAPIExtraTLSPadding()) { + networkRequest.setExtraTLSPadding(true); + } NetworkReply *reply; switch (request_->requestType()) { diff --git a/client/engine/engine/serverapi/serverapi.cpp b/client/engine/engine/serverapi/serverapi.cpp index 9a2f47f88..e1b3b9909 100644 --- a/client/engine/engine/serverapi/serverapi.cpp +++ b/client/engine/engine/serverapi/serverapi.cpp @@ -441,6 +441,9 @@ void ServerAPI::executeRequestImpl(QPointer request, const failover if (!failoverData.echConfig().isEmpty()) { networkRequest.setEchConfig(failoverData.echConfig()); } + if (ExtraConfig::instance().getAPIExtraTLSPadding()) { + networkRequest.setExtraTLSPadding(true); + } NetworkReply *reply; switch (request->requestType()) { diff --git a/client/gui/CMakeLists.txt b/client/gui/CMakeLists.txt index 45244a55b..56d9a53f1 100644 --- a/client/gui/CMakeLists.txt +++ b/client/gui/CMakeLists.txt @@ -21,6 +21,7 @@ endif() set(WS_TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/translations/ws_desktop_ar.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/ws_desktop_cs.ts ${CMAKE_CURRENT_SOURCE_DIR}/translations/ws_desktop_de.ts ${CMAKE_CURRENT_SOURCE_DIR}/translations/ws_desktop_es.ts ${CMAKE_CURRENT_SOURCE_DIR}/translations/ws_desktop_fr.ts diff --git a/client/gui/backend/preferences/preferenceshelper.cpp b/client/gui/backend/preferences/preferenceshelper.cpp index 5f1498b38..7647c1a88 100644 --- a/client/gui/backend/preferences/preferenceshelper.cpp +++ b/client/gui/backend/preferences/preferenceshelper.cpp @@ -6,7 +6,7 @@ PreferencesHelper::PreferencesHelper(QObject *parent) : QObject(parent), isWifiSharingSupported_(true), bIpv6StateInOS_(true), isFirewallBlocked_(false), isDockedToTray_(false), isExternalConfigMode_(false) { - availableLanguageCodes_ << "ar" << "de" << "en" << "es" << "fr" << "hi" << "ru" << "zh"; + availableLanguageCodes_ << "ar" << "cs" << "de" << "en" << "es" << "fr" << "hi" << "ru" << "zh"; } QString PreferencesHelper::buildVersion() diff --git a/client/gui/mainwindowcontroller.cpp b/client/gui/mainwindowcontroller.cpp index 1eb9bdb05..54f7995c6 100644 --- a/client/gui/mainwindowcontroller.cpp +++ b/client/gui/mainwindowcontroller.cpp @@ -1997,12 +1997,23 @@ void MainWindowController::gotoExitWindow(bool isLogout) || curWindow_ == WINDOW_ID_EMERGENCY || curWindow_ == WINDOW_ID_EXTERNAL_CONFIG || curWindow_ == WINDOW_ID_TWO_FACTOR_AUTH - || curWindow_ == WINDOW_ID_GENERAL_MESSAGE); + || curWindow_ == WINDOW_ID_GENERAL_MESSAGE + || curWindow_ == WINDOW_ID_LOGOUT); + // If we're overriding a logout with a quit (e.g. user pressed alt-f4 while on the logout prompt), + // close the previous logout window first + if (curWindow_ == WINDOW_ID_LOGOUT) { + // Suppress expanding preferences window even though originally logout came from prefs. + closeExitWindow(false); + } windowBeforeExit_ = curWindow_; IGeneralMessageWindow *win = (isLogout ? logoutWindow_ : exitWindow_); TooltipController::instance().hideAllTooltips(); + for (auto w : windowSizeManager_->windows()) { + collapseWindow(w, false, true); + } + if (curWindow_ == WINDOW_ID_CONNECT) { if (preferences_->appSkin() == APP_SKIN_VAN_GOGH) { win->setBackgroundShape(IGeneralMessageWindow::kConnectScreenVanGoghShape); diff --git a/client/gui/preferenceswindow/robertwindow/robertwindowitem.cpp b/client/gui/preferenceswindow/robertwindow/robertwindowitem.cpp index 0db9584c9..e16dc40e2 100644 --- a/client/gui/preferenceswindow/robertwindow/robertwindowitem.cpp +++ b/client/gui/preferenceswindow/robertwindow/robertwindowitem.cpp @@ -183,6 +183,7 @@ void RobertWindowItem::onLanguageChanged() void RobertWindowItem::onManageRobertRulesClick() { manageRulesItem_->setInProgress(true); + emit manageRobertRulesClick(); } void RobertWindowItem::setWebSessionCompleted() diff --git a/client/gui/translations/ws_desktop_cs.ts b/client/gui/translations/ws_desktop_cs.ts new file mode 100644 index 000000000..2c521abfd --- /dev/null +++ b/client/gui/translations/ws_desktop_cs.ts @@ -0,0 +1,2921 @@ + + + + + AdvancedParametersDialog + + + Write your parameters here + Sem napište své parametry + + + + Clear + Jasný + + + + Ok + Ok + + + + Cancel + Zrušit + + + + CommonGraphics::EscapeButton + + + + + ESC + ESC + + + + ConnectWindow::ConnectWindowItem + + + No Network Info + Žádné informace o síti + + + + Unsecured + Nezabezpečený + + + + Secured + Zabezpečený + + + + Connection to Windscribe has been terminated. + Připojení k Windscribe bylo ukončeno. + + + + transferred in + převedeno do + + + + Connected for + Připojeno pro + + + + transferred + převedený + + + + Firewall + Firewall + + + + Blocks all connectivity in the event of a sudden disconnect + Blokuje veškerou konektivitu v případě náhlého odpojení + + + + Connect to rate + Připojit k sazbě + + + + ConnectWindow::LocationsButton + + + Locations + Místa + + + + ConnectWindow::MiddleItem + + + + Firewall + Firewall + + + + CustomMenuWidget + + + Undo + Odčinit + + + + Redo + Předělat + + + + Cut + Řezat + + + + Copy + Kopírovat + + + + Paste + Pasta + + + + Delete + Vymazat + + + + Select All + Vybrat vše + + + + DialogGetUsernamePassword + + + Enter connection credentials + Zadejte pověření pro připojení + + + + Please enter the username + Zadejte prosím uživatelské jméno + + + + Please enter the password + Zadejte prosím heslo + + + + EmergencyConnectWindow::EmergencyConnectWindowItem + + + Emergency Connect + Tísňové připojení + + + + Emergency connection failure. Try again? + Selhání nouzového připojení. Zkuste to znovu? + + + + Can't access Windscribe.com or login into the app on your restrictive network? Connect to the emergency server that unblocks all of Windscribe. + Nemáte přístup k Windscribe.com nebo se nemůžete přihlásit k aplikaci v omezené síti? Připojte se k nouzovému serveru, který odblokuje všechny Windscribe. + + + + Connecting... + Připojování... + + + + Disconnecting... + Odpojující... + + + + Connect + Spojit + + + + Disconnect + Odpojit + + + + EmergencyConnectWindow::TextLinkButton + + + Access for Windscribe.com Only + Přístup pouze pro Windscribe.com + + + + Access for + Přístup pro + + + + Only + Pouze + + + + ExternalConfigWindow::ExternalConfigWindowItem + + + External Config Mode + Režim externí konfigurace + + + + Use the Windscribe app without an account to connect to any OpenVPN or WireGuard server. + Použijte aplikaci Windscribe bez účtu pro připojení k libovolnému serveru OpenVPN nebo WireGuard. + + + + Ok, got it! + Ok, rozumím! + + + + FreeTrafficNotificationController + + + + You reached %1% of your free bandwidth allowance + Dosáhli jste %1 % povolené volné šířky pásma + + + + GeneralMessageController + + + Ok + Ok + + + + Yes + Ano + + + + No + Ne + + + + Cancel + Zrušit + + + + GeneralMessageWindow::GeneralMessageItem + + + Ignore Warnings + Ignorovat upozornění + + + + Learn More + Víc se uč + + + + GuiLocations::LocationsTab + + + All + Všichni + + + + Configured + Nastavený + + + + Static IPs + Statické IP adresy + + + + Favourites + Oblíbené + + + + Search + Hledání + + + + Choose the directory that contains custom configs you wish to display here + Vyberte adresář, který obsahuje vlastní konfigurace, které zde chcete zobrazit + + + + Choose + Zvolit + + + + The selected directory contains no custom configs + Vybraný adresář neobsahuje žádné vlastní konfigurace + + + + No locations found + Nebyla nalezena žádná místa + + + + Nothing to see here... + Tady není nic k vidění... + + + + You don't have any Static IPs + Nemáte žádné statické IP adresy + + + + No locations + Žádné lokality + + + + Buy + Kupovat + + + + GuiLocations::StaticIPDeviceInfo + + + Add Static IP + Přidat statickou IP adresu + + + + LogViewer::LogViewerWindow + + + Merge all logs by timestamp + Sloučit všechny protokoly podle časového razítka + + + + Word Wrap + Zalamování + + + + Color highlighting + Barevné zvýraznění + + + + Export to file... + Exportovat do souboru... + + + + Save log + Uložit protokol + + + + Text files (*.txt) + Textové soubory (*.txt) + + + + Export log + Protokol exportu + + + + Failed to export log. Make sure you have the correct permissions. + Export protokolu se nezdařil. Ujistěte se, že máte správná oprávnění. + + + + LoginWindow::CredentialsWindowItem + + + Turn Off Firewall + Vypnout bránu firewall + + + + No Internet Connectivity + Žádné připojení k internetu + + + + No API Connectivity + Žádné připojení API + + + + Proxy requires authentication + Proxy vyžaduje ověření + + + + Invalid API response, check your network + Neplatná odpověď rozhraní API, zkontrolujte svou síť + + + + Invalid API Endpoint + Neplatný koncový bod rozhraní API + + + + ...hmm are you sure this is correct? + ... Hmm, jsi si jistý, že je to správně? + + + + ...Sorry, seems like it's wrong again + ... Omlouváme se, zdá se, že je to zase špatně + + + + ...hmm, try resetting your password! + ... Hmm, zkuste resetovat heslo! + + + + Rate limited. Please wait before trying to login again. + Sazba omezena. Před dalším pokusem o přihlášení počkejte. + + + + Session is expired. Please login again + Platnost relace vypršela. Přihlaste se prosím znovu + + + + Your username should not be an email address. Please try again. + Vaše uživatelské jméno by nemělo být e-mailovou adresou. Zkuste to prosím znovu. + + + + Login + Přihlášení do systému + + + + Emergency Connect is ON + Tísňové připojení je zapnuté + + + + Emergency Connect + Tísňové připojení + + + + External Config + Externí konfigurace + + + + Preferences + Možnosti + + + + 2FA Code + Kód 2FA + + + + Forgot password? + Zapomněli jste heslo? + + + + Username + Uživatelské jméno + + + + Password + Heslo + + + + LoginWindow::WelcomeWindowItem + + + Keep Your Secrets. + Uchovávejte svá tajemství. + + + + Emergency Connect + Tísňové připojení + + + + External Config + Externí konfigurace + + + + Preferences + Možnosti + + + + Turn Off Firewall + Vypnout bránu firewall + + + + Login + Přihlášení do systému + + + + Get Started + Rozjet + + + + MainWindow + + + + + Logging you in... + Přihlašuji vás... + + + + + VPN is active + VPN je aktivní + + + + Rotating your MAC address will result in a disconnect event from the current network. Are you sure? + Střídání adresy MAC bude mít za následek událost odpojení od aktuální sítě. Opravdu? + + + + Cannot detect appropriate packet size while connected. Please disconnect first. + Během připojení nelze zjistit odpovídající velikost paketu. Nejprve se odpojte ode sebe. + + + + No Internet + Žádný internet + + + + Cannot detect appropriate packet size without internet. Check your connection. + Nelze detekovat vhodnou velikost paketu bez internetu. Zkontrolujte připojení. + + + + Select Custom Config Folder + Vyberte vlastní konfigurační složku + + + + Cannot select this directory because it is writeable for non-privileged users. Custom configs in this directory may pose a potential security risk. Please authenticate with an admin user to select this directory. + Tento adresář nelze vybrat, protože je zapisovatelný pro neprivilegované uživatele. Vlastní konfigurace v tomto adresáři mohou představovat potenciální bezpečnostní riziko. Chcete-li vybrat tento adresář, ověřte se u uživatele s oprávněním správce. + + + + Can't select directory + Nelze vybrat adresář + + + + + The application is corrupted. Please reinstall Windscribe. + Aplikace je poškozena. Přeinstalujte program Windscribe. + + + + Validation Error + Chyba ověření + + + + The selected directory is writeable for non-privileged users. Custom configs in this directory may pose a potential security risk. + Vybraný adresář je zapisovatelný pro neprivilegované uživatele. Vlastní konfigurace v tomto adresáři mohou představovat potenciální bezpečnostní riziko. + + + + Security Risk + Bezpečnostní riziko + + + + Enable Service? + Povolit službu? + + + + Enable "Base Filtering Engine" service? This is required for Windscribe to function. + Povolit službu "Base Filtering Engine"? To je nutné pro fungování Windscribe. + + + + Failed to Enable Service + Nepodařilo se povolit službu + + + + Could not start 'Base Filtering Engine' service. Please enable this service manually in Windows Services. + Nelze spustit službu Base Filtering Engine. Tuto službu povolte ručně ve Službách systému Windows. + + + + Failed to Start + Nepodařilo se spustit + + + + + Windscribe is malfunctioning. Please restart the application. + Windscribe nefunguje správně. Restartujte aplikaci. + + + + Trying Backup Endpoints %1/%2 + Zkouší koncové body zálohování %1/%2 + + + + SSL Error + Chyba SSL + + + + We detected that SSL requests may be intercepted on your network. This could be due to a firewall configured on your computer, or Windscribe being blocked by your network administrator. Ignore SSL errors? + Zjistili jsme, že ve vaší síti mohou být zachyceny požadavky SSL. Příčinou může být brána firewall nakonfigurovaná v počítači nebo blokování programu Windscribe správcem sítě. Ignorovat chyby SSL? + + + + + Disconnected + Nesouvislý + + + + Connected to + Připojeno k + + + + You are now connected to Windscribe (%1). + Nyní jste připojeni k Windscribe (%1). + + + + Connection to Windscribe has been terminated. +%1 transferred in %2 + Připojení k Windscribe bylo ukončeno. +%1 převedeno v %2 + + + + Network Settings Interference + Rušení nastavení sítě + + + + We've detected that your network settings may interfere with Windscribe. Please send us a debug log to troubleshoot. + Zjistili jsme, že nastavení sítě může narušovat používání aplikace Windscribe. Zašlete nám prosím protokol ladění pro řešení problémů. + + + + + Send Debug Log + Odeslat protokol ladění + + + + Set “%1” as preferred protocol? + Nastavit "%1" jako preferovaný protokol? + + + + Windscribe will always use this protocol to connect on this network in the future to avoid any interruptions. + Windscribe bude tento protokol v budoucnu vždy používat pro připojení k této síti, aby nedošlo k přerušení. + + + + Set as Preferred + Nastavit jako preferované + + + + Service Error + Chyba služby + + + + Windscribe has detected that %1 is using a high amount of CPU due to a potential conflict with the VPN connection. Do you want to disable the Windscribe TCP socket termination feature that may be causing this issue? + Nástroj Windscribe zjistil, že %1 využívá velké množství procesoru z důvodu možného konfliktu s připojením VPN. Chcete zakázat funkci ukončení soketu TCP Windscribe, která může být příčinou tohoto problému? + + + + High CPU Usage + Vysoké využití procesoru + + + + + MAC Spoofing Failed + Falšování identity MAC se nezdařilo + + + + Your network adapter does not support MAC spoofing. Try a different adapter. + Síťový adaptér nepodporuje falšování adres MAC. Vyzkoušejte jiný adaptér. + + + + Could not spoof MAC address. Please try a different network interface or contact support. + Nelze zfalšovat adresu MAC. Zkuste použít jiné síťové rozhraní nebo kontaktujte podporu. + + + + Detection Error + Chyba detekce + + + + Cannot detect appropriate packet size due to an error. Please try again. + Nelze zjistit odpovídající velikost paketu z důvodu chyby. Zkuste to prosím znovu. + + + + This network hates us + Tato síť nás nenávidí + + + + We couldn’t connect you on this network. Send us your debug log so we can figure out what happened. + V této síti se nám nepodařilo vás připojit. Pošlete nám svůj protokol ladění, abychom mohli zjistit, co se stalo. + + + + Debug Sent! + Ladění odesláno! + + + + Your debug log has been received. Please contact support if you want assistance with this issue. + Byl přijat protokol ladění. Pokud potřebujete pomoc s tímto problémem, obraťte se na podporu. + + + + Contact Support + Kontaktujte podporu + + + + Auto-Update Failed + Automatická aktualizace se nezdařila + + + + Please contact support + Kontaktujte prosím podporu + + + + Could not download update. Please try again or use a different network. + Aktualizaci nelze stáhnout. Zkuste to znovu nebo použijte jinou síť. + + + + Could not run updater (Error %1). Please contact support + Nelze spustit updater (Chyba %1). Kontaktujte prosím podporu + + + + Lost connection to the backend process. +Recovering... + Došlo ke ztrátě připojení k back-endovému procesu. +Uzdravování... + + + + Select an application + Vyberte aplikaci + + + + Connect + Spojit + + + + Disconnect + Odpojit + + + + + Locations + Místa + + + + + Favourites + Oblíbené + + + + + Static IPs + Statické IP adresy + + + + + Configured + Nastavený + + + + Show/Hide + Zobrazit/skrýt + + + + Preferences + Možnosti + + + + Help + Pomoc + + + + Exit + Východ + + + + Read-only file + Soubor jen pro čtení + + + + Your hosts file is read-only. IKEv2 connectivity requires for it to be writable. Fix the issue automatically? + Soubor hosts je jen pro čtení. Připojení IKEv2 vyžaduje, aby bylo možné zapisovat. Opravit problém automaticky? + + + + The custom configuration could not be loaded. Please check that it’s correct or contact support. + Vlastní konfiguraci nelze načíst. Zkontrolujte správnost nebo kontaktujte podporu. + + + + There is a problem with the Wintun device driver, and it could not be reinstalled automatically. Please reinstall Windscribe. + Došlo k potížím s ovladačem zařízení Wintun a nelze jej automaticky přeinstalovat. Přeinstalujte program Windscribe. + + + + There is a problem with the TAP device driver, and it could not be reinstalled automatically. Please reinstall Windscribe. + Došlo k potížím s ovladačem zařízení TAP a nelze jej automaticky přeinstalovat. Přeinstalujte program Windscribe. + + + + WireGuard adapter setup failed. Please wait one minute and try the connection again. If adapter setup fails again, please try restarting your computer. + +If the problem persists after a restart, please send a debug log and open a support ticket, then switch to a different connection mode. + Nastavení adaptéru WireGuard se nezdařilo. Počkejte jednu minutu a zkuste připojení zopakovat. Pokud se nastavení adaptéru opět nezdaří, zkuste restartovat počítač. + +Pokud problém přetrvává i po restartování, odešlete protokol ladění, otevřete lístek podpory a poté přepněte do jiného režimu připojení. + + + + An unexpected error occurred establishing the VPN connection (Error %1). If this error persists, try using a different protocol or contact support. + Při vytváření připojení VPN došlo k neočekávané chybě (Chyba % 1). Pokud tato chyba přetrvává, zkuste použít jiný protokol nebo kontaktujte podporu. + + + + Connection Error + Chyba připojení + + + + Reached Key Limit + Dosažen limit klíče + + + + You have reached your limit of WireGuard public keys. Do you want to delete your oldest key? + Dosáhli jste limitu veřejných klíčů WireGuard. Chcete odstranit nejstarší klíč? + + + + Error Starting Service + Chyba při spuštění služby + + + + The split tunneling feature could not be started, and has been disabled in Preferences. + Funkci děleného tunelového propojení nelze spustit a byla zakázána v Předvolbách. + + + + Windscribe + Windscribe + + + + MainWindowController + + + Quit Windscribe? + Ukončit Windscribe? + + + + Log Out of Windscribe? + Odhlásit se z Windscribe? + + + + Quit + Přestat + + + + Log Out + Odhlášení ze systému + + + + Cancel + Zrušit + + + + NewsFeedWindow::EntryItem + + + Long ago + Dávno + + + + 1 day ago + Před 1 dnem + + + + Today + Dnes + + + + %1 days ago + %1 dny + + + + NewsFeedWindow::NewsFeedWindowItem + + + News Feed + Kanál vybraných příspěvků + + + + NotificationsController + + + WELCOME TO WINDSCRIBE + VÍTEJTE V WINDSCRIBE + + + + <p>You will find announcements and general Windscribe related news here. Perhaps even delicious cake, everyone loves cake!</p> + <p>Oznámení a obecné novinky týkající se Windscribe najdete zde. Možná i lahodný dort, každý miluje dort!</p> + + + + Preferences + + + + Invalid DNS Settings + Neplatná nastavení DNS + + + + 'Connected DNS' was not configured with a valid Upstream 1 (IP/DNS-over-HTTPS/TLS). DNS was reverted to ROBERT (default). + "Připojený DNS" nebyl nakonfigurován s platným nadřazeným serverem 1 (IP/DNS-over-HTTPS/TLS). DNS byl vrácen na ROBERT (výchozí). + + + + 'Connected DNS' was not configured with a valid Upstream 2 (IP/DNS-over-HTTPS/TLS). DNS was reverted to ROBERT (default). + "Připojený DNS" nebyl nakonfigurován s platným nadřazeným serverem 2 (IP/DNS-over-HTTPS/TLS). DNS byl vrácen na ROBERT (výchozí). + + + + PreferencesWindow::AboutWindowItem + + + About + Asi + + + + Status + Stav + + + + About Us + O nás + + + + Privacy Policy + Zásady ochrany osobních údajů + + + + Terms + Termíny + + + + Blog + Blog + + + + Jobs + Pracovní místa + + + + Software Licenses + Softwarové licence + + + + PreferencesWindow::AccountWindowItem + + + + Login to view your account info + Chcete-li zobrazit informace o účtu, přihlaste se + + + + + Login + Přihlášení do systému + + + + Account + Účet + + + + INFO + INFORMACE + + + + Username + Uživatelské jméno + + + + PLAN + PLÁN + + + + Expires On + Platnost vyprší dne + + + + Reset Date + Datum resetování + + + + Data Left + Data vlevo + + + + Manage Account + Spravovat účet + + + + PreferencesWindow::AdvancedWindowItem + + + Advanced Options + Rozšířené možnosti + + + + Restart Required + Je vyžadováno restartování + + + + In order to toggle IPv6, a computer restart is required. Do it now? + Chcete-li přepnout protokol IPv6, je vyžadován restart počítače. Udělej to hned? + + + + Restart later + Restartovat později + + + + Make advanced tweaks to the way the app functions. + Proveďte pokročilé úpravy způsobu, jakým aplikace funguje. + + + + Advanced Parameters + Pokročilé parametry + + + + Pick between the TAP and Wintun network adapters for OpenVPN connections. + Vyberte si mezi síťovými adaptéry TAP a Wintun pro připojení OpenVPN. + + + + TAP Driver + Ovladač TAP + + + + Enables / disables system-wide IPv6 connectivity. + Povolí/zakáže připojení IPv6 v celém systému. + + + + IPv6 + Protokol IPv6 + + + + Resolve server API address automatically, or use one provided by the Support team. + Vyřešte adresu rozhraní API serveru automaticky nebo použijte adresu poskytnutou týmem podpory. + + + + Ignore SSL certificate validation errors. + Ignorujte chyby ověření certifikátu SSL. + + + + Ignore SSL Errors + Ignorovat chyby SSL + + + + Prevents IKEv2 connections from dying (by time-out) by periodically pinging the server. + Zabraňuje umírání připojení IKEv2 (vypršení časového limitu) pravidelným příkazem ping na server. + + + + Client-side Keepalive + Udržování alive na straně klienta + + + + Windscribe uses this DNS server to resolve addresses outside the VPN. + Windscribe používá tento DNS server k překladu adres mimo VPN. + + + + Warning: Using "OS Default" may sometimes cause DNS leaks during reconnects. + Upozornění: Použití "Výchozí operační systém" může někdy způsobit únik DNS během opětovného připojení. + + + + App Internal DNS + Interní DNS aplikace + + + + Select the DNS system service Windscribe enforces. Experienced users only. + Vyberte systémovou službu DNS, kterou nástroj Windscribe vynucuje. Pouze zkušení uživatelé. + + + + DNS Manager + Správce DNS + + + + PreferencesWindow::ApiResolutionGroup + + + API Resolution + Rozlišení rozhraní API + + + + Automatic + Automatický + + + + Manual + Manuál + + + + Address + Adresa + + + + Enter IP or Hostname + Zadejte IP adresu nebo název hostitele + + + + PreferencesWindow::BackgroundSettingsGroup + + + + Disconnected + Nesouvislý + + + + + Connected + Připojený + + + + Background + Pozadí + + + + Country Flags + Vlajky zemí + + + + None + Žádný + + + + Custom + Zvyk + + + + PreferencesWindow::ConnectedDnsGroup + + + Connected DNS + Připojený DNS + + + + Upstream 1 + Proti proudu 1 + + + + + IP/DNS-over-HTTPS/TLS + IP/DNS-over-HTTPS/TLS + + + + Upstream 2 + Proti proudu 2 + + + + Split DNS + Rozdělení DNS + + + + Domains + Domény + + + + PreferencesWindow::ConnectionWindowItem + + + + Allow LAN Traffic + Povolit provoz v síti LAN + + + + Terminate Sockets + Ukončení zásuvek + + + + Connection + Připojení + + + + Exclusive + Výhradní + + + + Inclusive + Zahrnující + + + + Off + Pryč + + + + Network Options + Možnosti sítě + + + + Split Tunneling + Dělené tunelové propojení + + + + Proxy Settings + Nastavení proxy serveru + + + + Connects to last used location when the app launches or joins a network. + Připojí se k naposledy použitému umístění, když se aplikace spustí nebo připojí k síti. + + + + Auto-Connect + Automatické připojení + + + + Control the mode of behavior of the Windscribe firewall. + Ovládejte režim chování brány firewall Windscribe. + + + + Connection Mode + Režim připojení + + + + Automatically choose the VPN protocol, or select one manually. + Automaticky vyberte protokol VPN nebo jej vyberte ručně. + + + + Automatically determine the MTU for your connection, or manually override. + Automaticky určete jednotku MTU pro připojení nebo ručně přepište. + + + + Select the DNS server while connected to Windscribe. + Vyberte server DNS při připojení k aplikaci Windscribe. + + + + Allow access to local services and printers while connected to Windscribe. + Povolte přístup k místním službám a tiskárnám při připojení k aplikaci Windscribe. + + + + Spoof your device's physical address (MAC address). + Falšování fyzické adresy (MAC adresy) zařízení. + + + + Close all active TCP sockets when the VPN tunnel is established. + Zavřete všechny aktivní sokety TCP při vytvoření tunelu VPN. + + + + Configure your TV, gaming console, or other devices that support proxy servers. + Nakonfigurujte televizor, herní konzoli nebo jiná zařízení, která podporují proxy servery. + + + + + Settings Conflict + Konflikt nastavení + + + + Disabling Allow LAN Traffic will cause your proxy gateway to stop working. Do you want to disable the proxy? + Zakázání možnosti Povolit provoz LAN způsobí, že brána proxy přestane fungovat. Chcete zakázat proxy server? + + + + LAN traffic is currently blocked by the Windscribe firewall. Do you want to allow LAN traffic to bypass the firewall in order for this feature to work? + Provoz v síti LAN je v současné době blokován bránou firewall Windscribe. Chcete, aby přenosy v síti LAN obcházely bránu firewall, aby tato funkce fungovala? + + + + PreferencesWindow::DnsDomainsGroup + + + Domain already exists. Please enter a new domain. + Doména již existuje. Zadejte novou doménu. + + + + Incorrect domain name. Please enter a valid domain. + Nesprávný název domény. Zadejte platnou doménu. + + + + PreferencesWindow::DnsDomainsWindowItem + + + List of domains + Seznam domén + + + + Enter the IP and/or wildcards you wish to use for DNS split feature. + Zadejte IP adresu a / nebo zástupné znaky, které chcete použít pro funkci rozdělení DNS. + + + + Please log in to modify domains. + Chcete-li upravit domény, přihlaste se. + + + + PreferencesWindow::EmailItem + + + + Sent! + Poslal! + + + + + Add + Přidat + + + + Resend + Poslat zpátky + + + + Sending + Posílání + + + + Failed + Neúspěšný + + + + Get 10GB/Month of data and gain the ability to reset your password. + Získejte data 10 GB / měsíc a získejte možnost resetovat heslo. + + + + Please confirm your email + Potvrďte prosím svůj e-mail + + + + Email + E-mail + + + + PreferencesWindow::FirewallGroup + + + Firewall Mode + Režim brány firewall + + + + When? + Kdy? + + + + PreferencesWindow::GeneralWindowItem + + + General + Obecné + + + + Run Windscribe when your device starts. + Spusťte Windscribe při spuštění zařízení. + + + + Launch on Startup + Spustit při spuštění + + + + Launch Windscribe in a minimized state. + Spusťte aplikaci Windscribe v minimalizovaném stavu. + + + + Start Minimized + Začátek minimalizován + + + + Windscribe minimizes to system tray and no longer appears in the task bar. + Windscribe se minimalizuje na hlavním panelu a již se nezobrazuje na hlavním panelu. + + + + Close to Tray + V blízkosti od místa Tray + + + + Don't show the Windscribe icon in dock. + Nezobrazovat ikonu Windscribe v doku. + + + + Hide from Dock + Skrýt z doku + + + + Pin Windscribe near the system tray or menu bar. + Připněte Windscribe do blízkosti hlavního panelu nebo řádku nabídek. + + + + Docked + Zakotvený + + + + Display system-level notifications when connection events occur. + Zobrazení oznámení na úrovni systému, když dojde k událostem připojení. + + + + Show Notifications + Zobrazit oznámení + + + + Display a location's load. Shorter bars mean lesser load (usage). + Zobrazte zatížení místa. Kratší tyče znamenají menší zatížení (použití). + + + + Show Location Load + Zobrazit načtení polohy + + + + Arrange locations alphabetically, geographically, or by latency. + Uspořádejte místa abecedně, geograficky nebo podle latence. + + + + Location Order + Objednávka umístění + + + + Display latency as signal strength bars or in milliseconds. + Zobrazení latence jako pruhů síly signálu nebo v milisekundách. + + + + Latency Display + Zobrazení latence + + + + Localize Windscribe to supported languages. + Lokalizujte Windscribe do podporovaných jazyků. + + + + Language + Jazyk + + + + Choose between the classic GUI or the "earless" alternative GUI. + Vyberte si mezi klasickým grafickým uživatelským rozhraním nebo alternativním grafickým uživatelským rozhraním "bez uší". + + + + App Skin + Vzhled aplikace + + + + Customize the background of the main app screen. + Přizpůsobte pozadí hlavní obrazovky aplikace. + + + + Choose to receive stable, beta, or experimental builds. + Zvolte, zda chcete dostávat stabilní, beta nebo experimentální sestavení. + + + + Update Channel + Aktualizovat kanál + + + + Version + Verze + + + + PreferencesWindow::HelpWindowItem + + + Help + Pomoc + + + + All you need to know about Windscribe. + Vše, co potřebujete vědět o Windscribe. + + + + Knowledge Base + Znalostní báze + + + + Not as smart as a human, but can still answer your questions. + Není tak chytrý jako člověk, ale stále dokáže odpovědět na vaše otázky. + + + + Talk to Garry + Promluvte si s Garrym + + + + Stuck? Send us a ticket. + Přilepen? Pošlete nám lístek. + + + + Send Ticket + Odeslat vstupenku + + + + Best places to help and get help from other users. + Nejlepší místa, kde můžete pomoci a získat pomoc od ostatních uživatelů. + + + + Community Support + Komunitní podpora + + + + Reddit + Reddit + + + + Discord + Svár + + + + View Debug Log + Zobrazit protokol ladění + + + + Send Debug Log + Odeslat protokol ladění + + + + Sending log... + Odesílání protokolu... + + + + Sent, thanks! + Odesláno, díky! + + + + Failed! + Neúspěšný! + + + + PreferencesWindow::MacSpoofingGroup + + + Cannot spoof on 'No Interface' + Nelze falšovat na "Žádné rozhraní" + + + + You can only spoof an existing adapter. + Můžete zfalšovat pouze existující adaptér. + + + + Cannot spoof the current interface + Nelze zfalšovat aktuální rozhraní + + + + The current primary interface must match the selected interface to spoof. + Aktuální primární rozhraní musí odpovídat vybranému rozhraní, které chcete falšovat. + + + + No Interface + Žádné rozhraní + + + + MAC Spoofing + Falšování obsahu MAC + + + + MAC Address + Adresa MAC + + + + Interface + Rozhraní + + + + Auto-Rotate MAC + Automatické otáčení MAC + + + + PreferencesWindow::NetworkOptionsNetworkWindowItem + + + Windscribe auto-connects if the device connects to this network. + Windscribe se automaticky připojí, pokud se zařízení připojí k této síti. + + + + Auto-Secure + Automatické zabezpečení + + + + Preferred Protocol + Upřednostňovaný protokol + + + + Choose whether to connect using the recommended tunneling protocol, or to specify a protocol of your choice. + Zvolte, zda se chcete připojit pomocí doporučeného protokolu tunelového propojení, nebo zda chcete zadat protokol podle svého výběru. + + + + Forget Network + Zapomeňte na síť + + + + PreferencesWindow::NetworkOptionsWindowItem + + + Network Options + Možnosti sítě + + + + Windscribe will auto-disconnect when the device connects to a network tagged "Unsecured". + Windscribe se automaticky odpojí, když se zařízení připojí k síti označené jako "Nezabezpečené". + + + + Mark all newly encountered networks as Secured. + Označte všechny nově zjištěné sítě jako zabezpečené. + + + + Auto-Secure Networks + Automatické zabezpečení sítí + + + + No Network Detected + Nebyla zjištěna žádná síť + + + + CURRENT NETWORK + AKTUÁLNÍ SÍŤ + + + + OTHER NETWORKS + OSTATNÍ SÍTĚ + + + + Secured + Zabezpečený + + + + Unsecured + Nezabezpečený + + + + No Networks Detected. +Connect to a network first + Nebyly zjištěny žádné sítě. +Nejprve se připojte k síti + + + + PreferencesWindow::NewAddressItem + + + + Enter IP or Hostname + Zadejte IP adresu nebo název hostitele + + + + PreferencesWindow::PacketSizeGroup + + + Auto-Detect & Generate MTU + Automatická detekce a generování MTU + + + + Packet Size + Velikost paketu + + + + Automatic + Automatický + + + + Manual + Manuál + + + + + MTU + MTU + + + + PreferencesWindow::PlanItem + + + Unlimited Data + Neomezená data + + + + %1/Month + %1/měsíc + + + + Pro + Profík + + + + Upgrade + Upgrade + + + + PreferencesWindow::PreferencesTabControlItem + + + General + Obecné + + + + Account + Účet + + + + Connection + Připojení + + + + R.O.B.E.R.T. + R.O.B.E.R.T. + + + + Advanced Options + Rozšířené možnosti + + + + Help + Pomoc + + + + About + Asi + + + + Login + Přihlášení do systému + + + + Log Out + Odhlášení ze systému + + + + Quit + Přestat + + + + PreferencesWindow::ProtocolGroup + + + Automatic + Automatický + + + + Manual + Manuál + + + + Protocol + Protokol + + + + Port + Přístav + + + + PreferencesWindow::ProxyGatewayGroup + + + Proxy Gateway + Brána proxy + + + + Proxy Type + Typ proxy serveru + + + + PreferencesWindow::ProxyIpAddressItem + + + IP + IP + + + + Copied + Zkopírovaný + + + + PreferencesWindow::ProxySettingsGroup + + + Proxy + Plná moc + + + + + Address + Adresa + + + + + Port + Přístav + + + + + Username + Uživatelské jméno + + + + + Password + Heslo + + + + PreferencesWindow::ProxySettingsWindowItem + + + Proxy Settings + Nastavení proxy serveru + + + + If your network has a LAN proxy, configure it here. + Pokud má vaše síť proxy server LAN, nakonfigurujte jej zde. + + + + PreferencesWindow::RobertItem + + + Blocking + Blokace + + + + Allowing + Dovolující + + + + PreferencesWindow::RobertWindowItem + + + R.O.B.E.R.T. + R.O.B.E.R.T. + + + + R.O.B.E.R.T. is a customizable server-side domain and IP blocking tool. Select the block lists you wish to apply on all your devices by toggling the switch. + R.O.B.E.R.T. je přizpůsobitelný nástroj pro blokování domén na straně serveru a IP adres. Přepnutím přepínače vyberte seznamy bloků, které chcete použít na všech svých zařízeních. + + + + Could not retrieve R.O.B.E.R.T. preferences from server. Try again later. + Nelze načíst předvolby R.O.B.E.R.T. ze serveru. Zkuste to znovu později. + + + + Login to view or change R.O.B.E.R.T preferences + Přihlaste se pro zobrazení nebo změnu předvoleb R.O.B.E.R.T + + + + Login + Přihlášení do systému + + + + Manage Custom Rules + Správa vlastních pravidel + + + + PreferencesWindow::SearchLineEditItem + + + + Search + Hledání + + + + PreferencesWindow::SecureHotspotGroup + + + + Secure Hotspot + Zabezpečený hotspot + + + + Secure hotspot is not supported by your network adapter. + Zabezpečený hotspot není síťovým adaptérem podporován. + + + + Secure hotspot is not supported for IKEv2/WireGuard protocol and automatic connection mode. + Zabezpečený hotspot není podporován protokolem IKEv2/WireGuard a režimem automatického připojení. + + + + Share your secure Windscribe connection wirelessly. + Sdílejte své zabezpečené připojení Windscribe bezdrátově. + + + + SSID + SSID + + + + Enter SSID + Zadejte SSID + + + + Password + Heslo + + + + At least 8 characters + Alespoň 8 znaků + + + + PreferencesWindow::SelectImageItem + + + [no selection] + [bez výběru] + + + + Select an image + Vyberte obrázek + + + + PreferencesWindow::SplitTunnelingAddressesGroup + + + IP or hostname already exists. Please enter a new IP or hostname. + IP adresa nebo název hostitele již existuje. Zadejte novou IP adresu nebo název hostitele. + + + + Incorrect IP address/mask combination. Please enter a valid hostname or IP address in plain or CIDR notation. + Nesprávná kombinace IP adresy a masky. Zadejte platný název hostitele nebo IP adresu v prostém zápisu nebo zápisu CIDR. + + + + This IP address or range is reserved by Windscribe and can not be changed. + Tato IP adresa nebo rozsah je rezervován společností Windscribe a nelze jej změnit. + + + + PreferencesWindow::SplitTunnelingAddressesWindowItem + + + IPs & Hostnames + IP adresy a názvy hostitelů + + + + Enter the IP and/or hostnames you wish to include in or exclude from the VPN tunnel below. + Níže zadejte IP adresu a/nebo názvy hostitelů, které chcete zahrnout nebo vyloučit z tunelu VPN. + + + + Please log in to modify split tunneling rules. + Chcete-li změnit pravidla děleného tunelového propojení, přihlaste se. + + + + PreferencesWindow::SplitTunnelingAppsItem + + + Search/Add Apps + Vyhledávání/přidávání aplikací + + + + PreferencesWindow::SplitTunnelingAppsWindowItem + + + Apps + Apps + + + + Add the apps you wish to include in or exclude from the VPN tunnel below. + Níže přidejte aplikace, které chcete zahrnout nebo vyloučit z tunelu VPN. + + + + Please log in to modify split tunneling rules. + Chcete-li změnit pravidla děleného tunelového propojení, přihlaste se. + + + + PreferencesWindow::SplitTunnelingGroup + + + Service Not Installed + Služba není nainstalována + + + + The split tunneling driver is not installed. To enable this feature, try reinstalling the Windscribe application. + +If the reinstall does not help, please contact Windscribe support for assistance. + Není nainstalován ovladač rozděleného tunelového propojení. Chcete-li tuto funkci povolit, zkuste znovu nainstalovat aplikaci Windscribe. + +Pokud přeinstalace nepomůže, obraťte se na podporu Windscribe a požádejte o pomoc. + + + + Selected IPs and hostnames will not go through Windscribe when connected. + Vybrané IP adresy a názvy hostitelů nebudou po připojení procházet programem Windscribe. + + + + Selected apps, IPs, and hostnames will not go through Windscribe when connected. + Vybrané aplikace, IP adresy a názvy hostitelů nebudou po připojení procházet programem Windscribe. + + + + Only selected IPs and hostnames will go through Windscribe when connected. + Po připojení budou přes Windscribe procházet pouze vybrané IP adresy a názvy hostitelů. + + + + Only selected apps, IPs, and hostnames will go through Windscribe when connected. + Po připojení budou Windscribe procházet pouze vybrané aplikace, IP adresy a názvy hostitelů. + + + + Split Tunneling + Dělené tunelové propojení + + + + Mode + Režim + + + + Exclusive + Výhradní + + + + Inclusive + Zahrnující + + + + Apps + Apps + + + + IPs & Hostnames + IP adresy a názvy hostitelů + + + + PreferencesWindow::SplitTunnelingWindowItem + + + Split Tunneling + Dělené tunelové propojení + + + + Include or exclude apps and hostnames from the VPN tunnel. + Zahrňte nebo vylučte aplikace a názvy hostitelů z tunelu VPN. + + + + Firewall will not function in this mode. + Brána firewall nebude v tomto režimu fungovat. + + + + ProtocolWindow::ProtocolLineItem + + + NEXT UP IN %1s + DALŠÍ V %1S + + + + Connected to + Připojeno k + + + + Failed + Neúspěšný + + + + ProtocolWindow::ProtocolPromptItem + + + Cutting-edge protocol. + Špičkový protokol. + + + + An IPsec based tunneling protocol. + Protokol tunelového propojení založený na protokolu IPsec. + + + + Balanced speed and security. + Vyvážená rychlost a bezpečnost. + + + + Use it if OpenVPN UDP fails. + Použijte jej, pokud OpenVPN UDP selže. + + + + Disguises traffic as HTTPS with TLS. + Zamaskuje provoz jako HTTPS pomocí protokolu TLS. + + + + Wraps traffic with web sockets. + Zabalí provoz pomocí webových soketů. + + + + Change Protocol + Změnit protokol + + + + Quickly re-connect using a different protocol. + Rychle se znovu připojte pomocí jiného protokolu. + + + + Connection Failure! + Připojení se nezdařilo! + + + + The protocol you’ve chosen has failed to connect. Windscribe will attempt to reconnect using the first protocol below. + Protokol, který jste zvolili, se nepodařilo připojit. Windscribe se pokusí znovu připojit pomocí prvního níže uvedeného protokolu. + + + + Cancel + Zrušit + + + + QObject + + + + R.O.B.E.R.T. + R.O.B.E.R.T. + + + + + Custom + Zvyk + + + + OS Default + Výchozí operační systém + + + + + + + + + + + + + + + + UNKNOWN + NEZNÁMÝ + + + + Geography + Zeměpis + + + + Alphabet + Abeceda + + + + Latency + Latence + + + + Bars + Tyče + + + + Ms + Milisekunda + + + + Manual + Manuál + + + + + Automatic + Automatický + + + + Always On + Vždy zapnuto + + + + Before Connection + Před připojením + + + + After Connection + Po připojení + + + + None + Žádný + + + + Auto-detect + Automatická detekce + + + + Release + Uvolnit + + + + Beta + Beta + + + + Guinea Pig + Morče + + + + Internal + Interní + + + + Exclude + Vyloučit + + + + Include + Zahrnovat + + + + Alpha + Alfa + + + + Van Gogh + Van Gogh + + + + Failed to open file + Nepodařilo se otevřít soubor + + + + Invalid config format + Neplatný formát konfigurace + + + + Missing "Interface" section + Chybějící část "Rozhraní" + + + + Missing "Peer" section + Chybějící sekce "Peer" + + + + Missing "PrivateKey" in the "Interface" section + Chybí "PrivateKey" v části "Rozhraní" + + + + Missing "Address" in the "Interface" section + Chybějící "Adresa" v sekci "Rozhraní" + + + + Missing "DNS" in the "Interface" section + Chybí "DNS" v části "Rozhraní" + + + + Missing "PublicKey" in the "Peer" section + Chybí "PublicKey" v části "Peer" + + + + Missing "Endpoint" in the "Peer" section + Chybějící "koncový bod" v části "Peer" + + + + Static IPs + Statické IP adresy + + + + Custom Configs + Vlastní konfigurace + + + + Your application version is no longer supported. Please update to continue using Windscribe. + Verze vaší aplikace již není podporována. Chcete-li pokračovat v používání aplikace Windscribe, aktualizujte ji. + + + + Please upgrade to a Pro account to continue using Windscribe. + Chcete-li pokračovat v používání aplikace Windscribe, upgradujte na účet Pro. + + + + Your original account %1 has expired. Creating multiple accounts to bypass free tier limitations is prohibited. Please login into the original account and wait until the bandwidth is reset. You can also upgrade to Pro. + Platnost původního účtu %1 vypršela. Vytváření více účtů za účelem obejití omezení úrovně Free je zakázáno. Přihlaste se k původnímu účtu a počkejte, až se šířka pásma resetuje. Můžete také upgradovat na Pro. + + + + Your account is disabled for abuse. + Váš účet je deaktivován z důvodu zneužití. + + + + Firewall Disabled + Brána firewall zakázána + + + + Can't use the firewall when Split Tunneling is enabled + Nelze použít bránu firewall, pokud je povoleno dělené tunelové propojení + + + + Firewall Always On + Brána firewall je vždy zapnutá + + + + Can't turn the firewall off because "Always On" mode is enabled + Bránu firewall nelze vypnout, protože je povolen režim Always On + + + + Windscribe is already running on your computer, but appears to not be responding. + Aplikace Windscribe je již v počítači spuštěna, ale zdá se, že neodpovídá. + + + + You may need to kill the non-responding Windscribe app or reboot your computer to fix the issue. + Možná budete muset ukončit nereagující aplikaci Windscribe nebo restartovat počítač, abyste problém vyřešili. + + + + One or more files in the Windscribe application bundle have been suspiciously modified. Please re-install Windscribe. + Jeden nebo více souborů v sadě aplikací Windscribe bylo podezřele změněno. Nainstalujte prosím znovu Windscribe. + + + + QWidget + + + Unknown Config Error + Neznámá chyba konfigurace + + + + File Sharing Frowned Upon + Sdílení souborů se mračilo + + + + ServerRatingsTooltip + + + Rate speed + Rychlost rychlosti + + + + SharingFeatures::SharingFeaturesWindowItem + + + Sharing Features + Funkce sdílení + + + + Proxy Gateway + Brána proxy + + + + Secure Hotspot + Zabezpečený hotspot + + + + TwoFactorAuthWindow::TwoFactorAuthOkButton + + + Add + Přidat + + + + Login + Přihlášení do systému + + + + TwoFactorAuthWindow::TwoFactorAuthWindowItem + + + Two-factor Auth + Dvoufaktorové ověřování + + + + Use your app to get an authentication code, and enter it below + Pomocí aplikace získejte ověřovací kód a zadejte ho dole + + + + Please provide a 2FA code + Zadejte prosím kód 2FA + + + + Invalid 2FA code, please try again + Neplatný kód 2FA, zkuste to prosím znovu + + + + UpdateApp::UpdateAppItem + + + v + v + + + + UPDATE + AKTUALIZACE + + + + UpdateWindowItem + + + Updating + Aktualizace + + + + You're about to update Windscribe. The application will terminate the active connection and restart automatically. + Chystáte se aktualizovat Windscribe. Aplikace ukončí aktivní připojení a automaticky se restartuje. + + + + Your update is in progress, hang in there... + Vaše aktualizace probíhá, vydržte tam... + + + + Cancel + Zrušit + + + + + Later + Později + + + + Update + Aktualizace + + + + UpgradeWidget::UpgradeWidgetItem + + + + GET MORE DATA + ZÍSKEJTE VÍCE DAT + + + + EXT CONFIG MODE + REŽIM EXT CONFIG + + + + 0 DAYS LEFT + 0 ZBÝVÁ 0 DNÍ + + + + 1 DAY LEFT + ZBÝVÁ 1 DEN + + + + 2 DAYS LEFT + ZBÝVÁ 2 DNY + + + + 3 DAYS LEFT + ZBÝVÁ 3 DNY + + + + 4 DAYS LEFT + ZBÝVÁ 4 DNY + + + + 5 DAYS LEFT + 5 DNÍ ZBÝVÁ + + + + %1 DAYS LEFT + %1 ZBÝVÁ 1 DNÍ + + + + LOGIN + PŘIHLÁŠENÍ DO SYSTÉMU + + + + RENEW + OBNOVIT + + + + UpgradeWindow::UpgradeWindowItem + + + You're out of data! + Došla vám data! + + + + Don't leave your front door open. Upgrade or wait until next month to get your monthly data allowance back. + Nenechávejte vchodové dveře otevřené. Upgradujte nebo počkejte do příštího měsíce, než získáte zpět svůj měsíční datový limit. + + + + Get more data + Získání dalších dat + + + + I'm broke + Jsem na mizině + + + + gui_locations::LocationsModel + + + Best Location + Nejlepší umístění + + + + locationsmodel::PingIpsController + + + start ping by time for: %1 (%2 - %3) + Spusťte ping podle času pro: %1 (%2 - %3) + + + + ping new node: %1 (%2 - %3) + ping nový uzel: %1 (%2 - %3) + + + + ping successful: %1 (%2 - %3) %4ms + Úspěšný příkaz Ping: %1 (%2 - %3) %4ms + + + + discarding ping while connected: %1 (%2 - %3) + Zahození příkazu ping při připojení: %1 (%2 - %3) + + + + ping failed: %1 (%2 - %3) + Příkaz ping se nezdařil: %1 (%2 - %3) + + + diff --git a/client/gui/translations/ws_desktop_ru.ts b/client/gui/translations/ws_desktop_ru.ts index f47390d46..ea20e2d82 100644 --- a/client/gui/translations/ws_desktop_ru.ts +++ b/client/gui/translations/ws_desktop_ru.ts @@ -6,17 +6,17 @@ Write your parameters here - Напишите свои параметры здесь + Введите доп. параметры в это поле Clear - Ясный + Очистить Ok - Хорошо + ОК @@ -31,7 +31,7 @@ ESC - ЭКУ + Закрыть @@ -44,12 +44,12 @@ Unsecured - Необеспеченный + Не защищена Secured - Обеспеченных + Защищена @@ -64,12 +64,12 @@ Connected for - Подключено для + Подключено transferred - Переданы + передано @@ -79,12 +79,12 @@ Blocks all connectivity in the event of a sudden disconnect - Блокирует все подключения в случае внезапного отключения + Заблокирует все соединения в случае внезапного отключения VPN Connect to rate - Подключение к тарифу + Подключитесь, чтобы оценить сервер @@ -92,7 +92,7 @@ Locations - Местонахождения + Локации @@ -119,17 +119,17 @@ Cut - Резать + Вырезать Copy - Копировать + Скопировать Paste - Паста + Вставить @@ -139,7 +139,7 @@ Select All - Выберите Все + Выделить всё @@ -170,17 +170,17 @@ Emergency connection failure. Try again? - Аварийный сбой соединения. Повторить? + Экстренное подключение не установилось. Повторить? Can't access Windscribe.com or login into the app on your restrictive network? Connect to the emergency server that unblocks all of Windscribe. - Не удается получить доступ к Windscribe.com или войти в приложение в ограничительной сети? Подключитесь к аварийному серверу, который разблокирует весь Windscribe. + Не удается получить доступ к Windscribe.com или войти в приложение из-за ограничений сети? Подключитесь к специальному серверу, который разблокирует весь Windscribe. Connecting... - Соединительный... + Подключение... @@ -190,12 +190,12 @@ Connect - Соединять + Подключиться Disconnect - Разъединять + Отключиться @@ -203,12 +203,12 @@ Access for Windscribe.com Only - Доступ только для Windscribe.com + Доступ только к Windscribe.com Access for - Доступ для + Доступ к @@ -226,7 +226,7 @@ Use the Windscribe app without an account to connect to any OpenVPN or WireGuard server. - Используйте приложение Windscribe без учетной записи для подключения к любому серверу OpenVPN или WireGuard. + Используйте приложение без учетной записи Windscribe, для подключения к любому серверу OpenVPN или WireGuard. @@ -240,7 +240,7 @@ You reached %1% of your free bandwidth allowance - Вы достигли %1% от вашей бесплатной пропускной способности + Вы израсходовали %1% от вашего бесплатного пакета трафика @@ -248,7 +248,7 @@ Ok - Хорошо + ОК @@ -289,7 +289,7 @@ Configured - Сконфигурированный + Пользовательские @@ -304,7 +304,7 @@ Search - Искать + Поиск @@ -314,7 +314,7 @@ Choose - Выбирать + Выбрать @@ -324,7 +324,7 @@ No locations found - Местоположения не найдены + Локации не найдены @@ -344,7 +344,7 @@ Buy - Покупать + Купить @@ -360,7 +360,7 @@ Merge all logs by timestamp - Объединение всех журналов по метке времени + Объединить все журналы по метке времени @@ -370,12 +370,12 @@ Color highlighting - Цветная подсветка + Выделение цветом Export to file... - Экспорт в файл... + Экспортировать в файл... @@ -403,7 +403,7 @@ Turn Off Firewall - Отключение брандмауэра + Выключить брандмауэр @@ -413,7 +413,7 @@ No API Connectivity - Нет подключения API + Нет подключения к API @@ -433,12 +433,12 @@ ...hmm are you sure this is correct? - ... Хм, вы уверены, что это правильно? + ... хм, вы уверены, что данные корректны? ...Sorry, seems like it's wrong again - ... Извините, кажется, что это снова неправильно + ... извините, кажется, снова что-то не так @@ -448,12 +448,12 @@ Rate limited. Please wait before trying to login again. - Тариф ограничен. Пожалуйста, подождите, прежде чем пытаться войти в систему снова. + Ограничение по поличеству запросов. Пожалуйста, подождите, прежде чем пытаться войти снова. Session is expired. Please login again - Сеанс истек. Пожалуйста, войдите в систему еще раз + Сеанс истёк. Пожалуйста, войдите еще раз @@ -468,7 +468,7 @@ Emergency Connect is ON - Аварийное подключение включено + Аварийное подключение установлено @@ -478,17 +478,17 @@ External Config - Внешняя конфигурация + Пользовательская конфигурация Preferences - Предпочтения + Настройки 2FA Code - 2FA Код + 2FA-код @@ -521,17 +521,17 @@ External Config - Внешняя конфигурация + Пользовательская конфигурация Preferences - Предпочтения + Настройки Turn Off Firewall - Отключение брандмауэра + Выключить брандмауэр @@ -541,7 +541,7 @@ Get Started - Начало работы + Начать работу @@ -562,12 +562,12 @@ Rotating your MAC address will result in a disconnect event from the current network. Are you sure? - Поворот MAC-адреса приведет к событию отключения от текущей сети. Уверен? + Смена MAC-адреса приведет к отключению от текущей сети. Продолжить? Cannot detect appropriate packet size while connected. Please disconnect first. - Не удается определить соответствующий размер пакета при подключении. Пожалуйста, сначала отключитесь. + Невозможно определить оптимальный размер пакета в подключённом состоянии. Пожалуйста, отключитесь сначала. @@ -577,7 +577,7 @@ Cannot detect appropriate packet size without internet. Check your connection. - Не удается определить соответствующий размер пакета без интернета. Проверьте подключение. + Невозможно определить оптимальный размер пакета из-за отсутствия интернета. Проверьте подключение. @@ -587,7 +587,7 @@ Cannot select this directory because it is writeable for non-privileged users. Custom configs in this directory may pose a potential security risk. Please authenticate with an admin user to select this directory. - Не удается выбрать этот каталог, так как он доступен для записи для непривилегированных пользователей. Пользовательские конфигурации в этом каталоге могут представлять потенциальную угрозу безопасности. Пожалуйста, аутентифицируйтесь с пользователем администратора, чтобы выбрать этот каталог. + Не удается выбрать этот каталог, так как он доступен на запись для непривилегированных пользователей. Пользовательские конфигурации в этом каталоге могут представлять потенциальную угрозу безопасности. Пожалуйста, аутентифицируйтесь под администратором для выбора этого каталога. @@ -598,12 +598,12 @@ Could not start 'Base Filtering Engine' service. Please enable this service manually in Windows Services. - Не удалось запустить службу 'Base Filtering Engine'. Включите эту службу вручную в службах Windows. + Не удалось запустить службу 'Base Filtering Engine'. Включите эту службу вручную в службах Windows. We detected that SSL requests may be intercepted on your network. This could be due to a firewall configured on your computer, or Windscribe being blocked by your network administrator. Ignore SSL errors? - Мы обнаружили, что SSL-запросы могут быть перехвачены в вашей сети. Это может быть связано с тем, что на вашем компьютере настроен брандмауэр, или Windscribe заблокирован сетевым администратором. Игнорировать ошибки SSL? + Мы обнаружили, что SSL-запросы в вашей сети перехватываются. Это может быть связано с тем, что на вашем компьютере настроен брандмауэр, или Windscribe заблокирован сетевым администратором. Игнорировать ошибки SSL? @@ -624,7 +624,7 @@ Could not download update. Please try again or use a different network. - Не удалось загрузить обновление. Повторите попытку или используйте другую сеть. + Не удалось загрузить обновление. Повторите попытку или воспользуйтесь другой сетью. @@ -659,7 +659,7 @@ Windscribe - Виндписец + Windscribe @@ -669,12 +669,12 @@ Validation Error - Ошибка проверки + Ошибка валидации The selected directory is writeable for non-privileged users. Custom configs in this directory may pose a potential security risk. - Выбранный каталог доступен для записи для непривилегированных пользователей. Пользовательские конфигурации в этом каталоге могут представлять потенциальную угрозу безопасности. + Выбранный каталог доступен на запись для непривилегированных пользователей. Пользовательские конфигурации в этом каталоге могут представлять потенциальную угрозу безопасности. @@ -689,7 +689,7 @@ Enable "Base Filtering Engine" service? This is required for Windscribe to function. - Включить услугу "Базовый механизм фильтрации"? Это необходимо для работы Windscribe. + Включить службу "Base Filtering Engine"? Это необходимо для работы Windscribe. @@ -699,12 +699,12 @@ Failed to Start - Не удалось запустить + Не удалось запуститься Trying Backup Endpoints %1/%2 - Попытка резервного копирования конечных точек %1/%2 + Пробуем резервные домены %1/%2 @@ -715,7 +715,7 @@ Disconnected - Бессвязный + Отключено @@ -725,19 +725,19 @@ You are now connected to Windscribe (%1). - Теперь вы подключены к Windscribe (%1). + Вы подключены к Windscribe (%1). Connection to Windscribe has been terminated. %1 transferred in %2 Соединение с Windscribe было прервано. -%1 переведено в %2 +%1 передано за %2 Network Settings Interference - Помехи сетевых настроек + Несовместимость сетевых настроек @@ -747,7 +747,7 @@ Windscribe will always use this protocol to connect on this network in the future to avoid any interruptions. - Windscribe всегда будет использовать этот протокол для подключения к этой сети в будущем, чтобы избежать каких-либо перерывов. + Windscribe всегда будет использовать этот протокол для подключения из текущей сети в будущем, чтобы избежать каких-либо проблем. @@ -762,7 +762,7 @@ Windscribe has detected that %1 is using a high amount of CPU due to a potential conflict with the VPN connection. Do you want to disable the Windscribe TCP socket termination feature that may be causing this issue? - Windscribe обнаружил, что %1 использует большое количество ЦП из-за потенциального конфликта с VPN-подключением. Вы хотите отключить функцию завершения сокета TCP Windscribe, которая может вызывать эту проблему? + Windscribe обнаружил, что %1 нагружает процессор из-за потенциального конфликта с VPN-подключением. Хотите отключить функцию закрытия TCP-соединений Windscribe, которая может вызывать эту проблему? @@ -773,12 +773,12 @@ MAC Spoofing Failed - Спуфинг MAC не удался + Подделка MAC-адреса неуспешна Your network adapter does not support MAC spoofing. Try a different adapter. - Сетевой адаптер не поддерживает спуфинг MAC-адресов. Попробуйте использовать другой адаптер. + Сетевой адаптер не поддерживает подделку MAC-адресов. Попробуйте выбрать другой адаптер. @@ -788,17 +788,17 @@ Cannot detect appropriate packet size due to an error. Please try again. - Не удается определить соответствующий размер пакета из-за ошибки. Повторите попытку. + Не удается определить оптимальный размер пакета из-за ошибки. Повторите попытку. This network hates us - Эта сеть ненавидит нас + Эта сеть нас ненавидит We couldn’t connect you on this network. Send us your debug log so we can figure out what happened. - Мы не смогли подключить вас к этой сети. Отправьте нам свой журнал отладки, чтобы мы могли выяснить, что произошло. + Мы не смогли подключить вас к VPN из этой сети. Отправьте нам журнал отладки, чтобы мы могли выяснить причины произошедшего. @@ -809,7 +809,7 @@ Debug Sent! - Отладка отправлена! + Журнал отправлен! @@ -846,18 +846,18 @@ Recovering... Connect - Соединять + Подключиться Disconnect - Разъединять + Отключиться Locations - Местонахождения + Локации @@ -875,7 +875,7 @@ Recovering... Configured - Сконфигурированный + Пользовательские @@ -885,7 +885,7 @@ Recovering... Preferences - Предпочтения + Настройки @@ -900,16 +900,16 @@ Recovering... Your hosts file is read-only. IKEv2 connectivity requires for it to be writable. Fix the issue automatically? - Файл hosts доступен только для чтения. Подключение IKEv2 требует, чтобы оно было доступным для записи. Устранить проблему автоматически? + Файл hosts доступен только для чтения. Подключению IKEv2 требуется, чтобы файл был доступным для записи. Устранить проблему автоматически? WireGuard adapter setup failed. Please wait one minute and try the connection again. If adapter setup fails again, please try restarting your computer. If the problem persists after a restart, please send a debug log and open a support ticket, then switch to a different connection mode. - Не удалось настроить адаптер WireGuard. Подождите одну минуту и повторите попытку подключения. Если установка адаптера снова не удается, попробуйте перезагрузить компьютер. + Не удалось настроить адаптер WireGuard. Подождите одну минуту и повторите попытку подключения. Если настройка адаптера снова не сработает, попробуйте перезагрузить компьютер. -Если проблема не устранена после перезагрузки, отправьте журнал отладки и откройте запрос в службу поддержки, а затем переключитесь в другой режим подключения. +Если проблема не устранена после перезагрузки, отправьте журнал отладки и откройте запрос в службу поддержки, а затем переключитесь на другой режим подключения. @@ -924,7 +924,7 @@ If the problem persists after a restart, please send a debug log and open a supp You have reached your limit of WireGuard public keys. Do you want to delete your oldest key? - Вы достигли лимита открытых ключей WireGuard. Вы хотите удалить свой самый старый ключ? + Вы достигли лимита открытых ключей WireGuard. Хотите удалить свой самый старый ключ? @@ -934,7 +934,7 @@ If the problem persists after a restart, please send a debug log and open a supp The split tunneling feature could not be started, and has been disabled in Preferences. - Не удалось запустить функцию раздельного туннелирования, которая была отключена в настройках. + Не удалось запустить функцию раздельного туннелирования, функция была автоматически отключена в настройках. @@ -942,22 +942,22 @@ If the problem persists after a restart, please send a debug log and open a supp Quit Windscribe? - Бросить Windscribe? + Покинуть Windscribe? Log Out of Windscribe? - Выйти из Windscribe? + Выйти из учётной записи Windscribe? Quit - Покидать + Закрыть программу Log Out - Выходить + Выйти из учётной записи @@ -985,7 +985,7 @@ If the problem persists after a restart, please send a debug log and open a supp %1 days ago - %1 день назад + %1 дней назад @@ -1006,7 +1006,7 @@ If the problem persists after a restart, please send a debug log and open a supp <p>You will find announcements and general Windscribe related news here. Perhaps even delicious cake, everyone loves cake!</p> - <p>Вы найдете объявления и общие новости, связанные с Windscribe здесь. Пожалуй, даже вкусный торт, все любят торт!</p> + <p>Здесь вы найдете объявления и общие новости, связанные с Windscribe. А может даже вкусный торт, все любят торт!</p> @@ -1025,7 +1025,7 @@ If the problem persists after a restart, please send a debug log and open a supp 'Connected DNS' was not configured with a valid Upstream 2 (IP/DNS-over-HTTPS/TLS). DNS was reverted to ROBERT (default). - 'Подключенный DNS' не был настроен с допустимым Upstream 2 (IP/DNS-over-HTTPS/TLS). DNS был возвращен к ROBERT (по умолчанию). + 'Подключенный DNS' не был настроен с допустимым апстримом 2 (IP/DNS-over-HTTPS/TLS). DNS был перенастроен на ROBERT (по умолчанию). @@ -1033,7 +1033,7 @@ If the problem persists after a restart, please send a debug log and open a supp About - Около + О программе @@ -1063,12 +1063,12 @@ If the problem persists after a restart, please send a debug log and open a supp Jobs - Рабочих мест + Вакансии Software Licenses - Лицензии на программное обеспечение + Лицензии ПО @@ -1083,7 +1083,7 @@ If the problem persists after a restart, please send a debug log and open a supp Login - Логин + Войти @@ -1108,7 +1108,7 @@ If the problem persists after a restart, please send a debug log and open a supp Expires On - Срок действия истекает + Срок действия до @@ -1131,7 +1131,7 @@ If the problem persists after a restart, please send a debug log and open a supp Advanced Options - Дополнительные опции + Расширенные опции @@ -1141,17 +1141,17 @@ If the problem persists after a restart, please send a debug log and open a supp In order to toggle IPv6, a computer restart is required. Do it now? - Чтобы переключить IPv6, требуется перезагрузка компьютера. Сделать это сейчас? + Чтобы переключить IPv6, требуется перезагрузка компьютера. Выполнить сейчас? Restart later - Перезагрузка позже + Перезагрузить позже Make advanced tweaks to the way the app functions. - Внесите дополнительные изменения в работу приложения. + Внесение дополнительных изменений в работу приложения. @@ -1161,7 +1161,7 @@ If the problem persists after a restart, please send a debug log and open a supp Pick between the TAP and Wintun network adapters for OpenVPN connections. - Выбирайте между сетевыми адаптерами TAP и Wintun для подключений OpenVPN. + Выбор между сетевыми адаптерами TAP и Wintun для подключений OpenVPN. @@ -1176,17 +1176,17 @@ If the problem persists after a restart, please send a debug log and open a supp IPv6 - Протокол IPv6 + IPv6 Resolve server API address automatically, or use one provided by the Support team. - Автоматическое разрешение API-адреса сервера или использование адреса, предоставленного службой поддержки. + Выбор сервера API автоматически или использование адреса, предоставленного службой поддержки. Ignore SSL certificate validation errors. - Игнорируйте ошибки проверки SSL-сертификата. + Игнорировать ошибки проверки SSL-сертификатов. @@ -1196,22 +1196,22 @@ If the problem persists after a restart, please send a debug log and open a supp Prevents IKEv2 connections from dying (by time-out) by periodically pinging the server. - Предотвращает гибель соединений IKEv2 (по истечению времени ожидания) путем периодического пинга сервера. + Предотвращает разрыв соединений IKEv2 по таймауту путем периодического пинга сервера. Client-side Keepalive - Клиентский Keepalive + Поддержание соединения Windscribe uses this DNS server to resolve addresses outside the VPN. - Windscribe использует этот DNS-сервер для разрешения адресов за пределами VPN. + Windscribe использует этот DNS-сервер для разрешения адресов вне VPN. Warning: Using "OS Default" may sometimes cause DNS leaks during reconnects. - Предупреждение: Использование «ОС по умолчанию» иногда может привести к утечкам DNS во время повторных подключений. + Предупреждение: использование «штатного» иногда может привести к утечкам DNS во время повторных подключений. @@ -1221,7 +1221,7 @@ If the problem persists after a restart, please send a debug log and open a supp Select the DNS system service Windscribe enforces. Experienced users only. - Выберите принудительную системную службу DNS Windscribe. Только для опытных пользователей. + Выбор системной службы DNS, которую настроит Windscribe. Только для опытных пользователей. @@ -1234,7 +1234,7 @@ If the problem persists after a restart, please send a debug log and open a supp API Resolution - Разрешение API + Сервер API @@ -1263,18 +1263,18 @@ If the problem persists after a restart, please send a debug log and open a supp Disconnected - Бессвязный + Отключено Connected - Связанный + Подключено Background - Фон + Фоновая картинка @@ -1284,12 +1284,12 @@ If the problem persists after a restart, please send a debug log and open a supp None - Никакой + Ничего Custom - Обычай + Собственная @@ -1297,12 +1297,12 @@ If the problem persists after a restart, please send a debug log and open a supp Connected DNS - Подключенный DNS + DNS в туннеле Upstream 1 - Разведка и добыча 1 + Сервер 1 @@ -1313,12 +1313,12 @@ If the problem persists after a restart, please send a debug log and open a supp Upstream 2 - Разведка и добыча 2 + Сервер 2 Split DNS - Разделение DNS + Раздельный DNS @@ -1337,7 +1337,7 @@ If the problem persists after a restart, please send a debug log and open a supp Terminate Sockets - Завершение сокетов + Закрытие сокетов @@ -1347,17 +1347,17 @@ If the problem persists after a restart, please send a debug log and open a supp Exclusive - Исключительный + Исключающий Inclusive - Включительно + Включающий Off - От + Выкл. @@ -1377,7 +1377,7 @@ If the problem persists after a restart, please send a debug log and open a supp Connects to last used location when the app launches or joins a network. - Подключается к последнему используемому расположению при запуске приложения или присоединении к сети. + Подключаться к последней используемой локации при запуске приложения или присоединении к сети. @@ -1397,7 +1397,7 @@ If the problem persists after a restart, please send a debug log and open a supp Automatically choose the VPN protocol, or select one manually. - Автоматически выберите протокол VPN или вручную. + Выбор VPN-протокола автоматически или вручную @@ -1417,17 +1417,17 @@ If the problem persists after a restart, please send a debug log and open a supp Spoof your device's physical address (MAC address). - Подделайте физический адрес (MAC-адрес) устройства. + Подделка физичесого адреса (MAC-адреса) устройства. Close all active TCP sockets when the VPN tunnel is established. - Закройте все активные TCP-сокеты при установке VPN-туннеля. + Закрытие всех активных TCP-соединений при установке VPN-туннеля. Configure your TV, gaming console, or other devices that support proxy servers. - Настройте телевизор, игровую консоль или другие устройства, поддерживающие прокси-серверы. + Настройка телевизора, игровой консоли и других устройств, поддерживающих прокси-серверы. @@ -1438,12 +1438,12 @@ If the problem persists after a restart, please send a debug log and open a supp Disabling Allow LAN Traffic will cause your proxy gateway to stop working. Do you want to disable the proxy? - Отключение параметра Разрешить трафик локальной сети приведет к тому, что прокси-шлюз перестанет работать. Вы хотите отключить прокси? + Отключение параметра "Разрешить трафик локальной сети" приведет к тому, что прокси-шлюз перестанет работать. Вы хотите отключить прокси? LAN traffic is currently blocked by the Windscribe firewall. Do you want to allow LAN traffic to bypass the firewall in order for this feature to work? - Трафик локальной сети в настоящее время блокируется брандмауэром Windscribe. Вы хотите разрешить трафику локальной сети обходить брандмауэр, чтобы эта функция работала? + Трафик локальной сети в настоящее время блокируется брандмауэром Windscribe. Вы хотите разрешить трафику локальной сети обходить брандмауэр, чтобы эта функция заработала? @@ -1469,7 +1469,7 @@ If the problem persists after a restart, please send a debug log and open a supp Enter the IP and/or wildcards you wish to use for DNS split feature. - Введите IP-адрес и/или подстановочные знаки, которые вы хотите использовать для функции разделения DNS. + Введите IP-адрес и/или шаблон, которые вы хотите использовать для функции раздельного DNS. @@ -1483,33 +1483,33 @@ If the problem persists after a restart, please send a debug log and open a supp Sent! - Посылать! + Отправить! Add - Добавлять + Добавить Resend - Отправить + Отправить заново Sending - Посылка + Отправка Failed - Неудавшийся + Неудачно Get 10GB/Month of data and gain the ability to reset your password. - Получите 10 ГБ / месяц данных и получите возможность сбросить пароль. + Получите 10 ГБ месячного трафика и возможность сброса пароля. @@ -1519,7 +1519,7 @@ If the problem persists after a restart, please send a debug log and open a supp Email - Отправить по электронной почте + Емейл @@ -1545,87 +1545,87 @@ If the problem persists after a restart, please send a debug log and open a supp Run Windscribe when your device starts. - Запустите Windscribe при запуске устройства. + Запустить Windscribe при включении устройства. Launch on Startup - Запуск при запуске + Запуск при включении ОС Launch Windscribe in a minimized state. - Запустите Windscribe в свернутом состоянии. + Запустить Windscribe в свернутом состоянии. Start Minimized - Запуск свернут + Запускать свернутым Windscribe minimizes to system tray and no longer appears in the task bar. - Windscribe сворачивается в системный трей и больше не отображается на панели задач. + Windscribe свернётся в системный трей и больше не будет отображаться на панели задач. Close to Tray - Рядом с лотком + Закрывать в трей Don't show the Windscribe icon in dock. - Не показывайте значок Windscribe в Dock. + Не показывать значок Windscribe на панели задач. Hide from Dock - Скрыться от Dock + Скрыть с панели Pin Windscribe near the system tray or menu bar. - Закрепите Windscribe рядом с системным треем или строкой меню. + Закрепить Windscribe рядом с системным треем или строкой меню. Docked - Закрепить + Закреплённый Display system-level notifications when connection events occur. - Отображение уведомлений системного уровня при возникновении событий подключения. + Отображать уведомления в системе о событиях, связанных с соединением. Show Notifications - Показать уведомления + Показывать уведомления Display a location's load. Shorter bars mean lesser load (usage). - Отображение нагрузки местоположения. Более короткие стержни означают меньшую нагрузку (использование). + Отображать степень загруженности локаций. Короткая полоса означает меньшую загруженность серверов. Show Location Load - Показать загрузку местоположения + Отображать нагрузку локаций Arrange locations alphabetically, geographically, or by latency. - Упорядочивайте местоположения в алфавитном порядке, географически или по задержке. + Упорядочить локации в алфавитном порядке, географически или по задержке. Location Order - Расположение Заказ + Сортировка локаций Display latency as signal strength bars or in milliseconds. - Отображение задержки в виде полос уровня сигнала или в миллисекундах. + Отображать задержку в виде полос или в миллисекундах. @@ -1645,22 +1645,22 @@ If the problem persists after a restart, please send a debug log and open a supp Choose between the classic GUI or the "earless" alternative GUI. - Выбирайте между классическим графическим интерфейсом или «безухим» альтернативным графическим интерфейсом. + Выбор между классическим графическим интерфейсом и «безухим» альтернативным интерфейсом. App Skin - Обложка приложения + Скин приложения Customize the background of the main app screen. - Настройте фон главного экрана приложения. + Настройка фона главного экрана приложения. Choose to receive stable, beta, or experimental builds. - Выберите получение стабильных, бета- или экспериментальных сборок. + Выбор получения стабильных, бета- или экспериментальных сборок. @@ -1698,37 +1698,37 @@ If the problem persists after a restart, please send a debug log and open a supp Talk to Garry - Поговорите с Гарри + Поговорить с Гарри Stuck? Send us a ticket. - Застрявший? Пришлите нам билет. + Возникли сложности? Пришлите сообщение о проблеме. Send Ticket - Отправить билет + Сообщить о проблеме Best places to help and get help from other users. - Лучшие места, чтобы помочь и получить помощь от других пользователей. + Места, где можно помочь и получить помощь от других пользователей. Community Support - Поддержка сообщества + Поддержка силами сообщества Reddit - Реддит + Reddit Discord - Разногласие + Discord @@ -1748,12 +1748,12 @@ If the problem persists after a restart, please send a debug log and open a supp Sent, thanks! - Прислали, спасибо! + Отправлено, спасибо! Failed! - Неудавшийся! + Ошибка! @@ -1776,7 +1776,7 @@ If the problem persists after a restart, please send a debug log and open a supp The current primary interface must match the selected interface to spoof. - Текущий основной интерфейс должен соответствовать выбранному интерфейсу spoof. + Текущий основной интерфейс должен соответствовать выбранному подделываемому интерфейсу. @@ -1786,7 +1786,7 @@ If the problem persists after a restart, please send a debug log and open a supp MAC Spoofing - Спуфинг MAC + Подделка MAC @@ -1801,7 +1801,7 @@ If the problem persists after a restart, please send a debug log and open a supp Auto-Rotate MAC - Автоматический поворот MAC + Автосмена MAC @@ -1809,7 +1809,7 @@ If the problem persists after a restart, please send a debug log and open a supp Windscribe auto-connects if the device connects to this network. - Windscribe автоматически подключается, если устройство подключается к этой сети. + Windscribe автоматически подключится, если устройство соединиться с этой сетью. @@ -1829,7 +1829,7 @@ If the problem persists after a restart, please send a debug log and open a supp Forget Network - Забудьте о сети + Забыть сеть @@ -1842,12 +1842,12 @@ If the problem persists after a restart, please send a debug log and open a supp Windscribe will auto-disconnect when the device connects to a network tagged "Unsecured". - Windscribe автоматически отключается, когда устройство подключается к сети с пометкой «Незащищенный». + Windscribe автоматически отключится, когда устройство подключается к сети с пометкой «Незащищенная». Mark all newly encountered networks as Secured. - Пометьте все вновь обнаруженные сети как защищенные. + Помечать все вновь обнаруженные сети как защищенные. @@ -1872,12 +1872,12 @@ If the problem persists after a restart, please send a debug log and open a supp Secured - Обеспеченных + Защищена Unsecured - Необеспеченный + Не защищена @@ -1901,7 +1901,7 @@ Connect to a network first Auto-Detect & Generate MTU - Автоматическое обнаружение и генерация MTU + Автоматическое обнаружение оптимального MTU @@ -1922,7 +1922,7 @@ Connect to a network first MTU - МТУ + MTU @@ -1930,7 +1930,7 @@ Connect to a network first Unlimited Data - Неограниченные данные + Неограниченный пакет трафика @@ -1940,12 +1940,12 @@ Connect to a network first Pro - Профессионал + Про Upgrade - Модернизировать + Улучшить @@ -1958,7 +1958,7 @@ Connect to a network first Account - Счет + Учётная запись @@ -1968,12 +1968,12 @@ Connect to a network first R.O.B.E.R.T. - Р.О.Б.Е.Р.Т. + R.O.B.E.R.T. Advanced Options - Дополнительные опции + Расширенные настройки @@ -1983,22 +1983,22 @@ Connect to a network first About - Около + О программе Login - Логин + Войти Log Out - Выходить + Выйти из учётной записи Quit - Покидать + Закрыть программу @@ -2042,12 +2042,12 @@ Connect to a network first IP - Протокол IP + IP Copied - Скопированы + Скопировано @@ -2055,7 +2055,7 @@ Connect to a network first Proxy - Доверенность + Прокси @@ -2092,7 +2092,7 @@ Connect to a network first If your network has a LAN proxy, configure it here. - Если в вашей сети есть прокси-сервер локальной сети, настройте его здесь. + Если в вашей сети есть прокси-сервер, настройте его здесь. @@ -2100,12 +2100,12 @@ Connect to a network first Blocking - Блокировка + Блокируется Allowing - Позволяя + Не блокируется @@ -2113,12 +2113,12 @@ Connect to a network first R.O.B.E.R.T. - Р.О.Б.Е.Р.Т. + R.O.B.E.R.T. R.O.B.E.R.T. is a customizable server-side domain and IP blocking tool. Select the block lists you wish to apply on all your devices by toggling the switch. - R.O.B.E.R.T. - это настраиваемый инструмент блокировки доменов на стороне сервера и IP-адресов. Выберите списки блокировки, которые вы хотите применить на всех своих устройствах, переключив переключатель. + R.O.B.E.R.T. - это настраиваемый инструмент блокировки доменов и IP-адресов на стороне сервера. Выберите списки, которые вы хотите заблокировать на всех своих устройствах. @@ -2128,7 +2128,7 @@ Connect to a network first Login to view or change R.O.B.E.R.T preferences - Вход для просмотра или изменения настроек R.O.B.E.R.T + Войдите для просмотра или изменения настроек R.O.B.E.R.T @@ -2318,12 +2318,12 @@ If the reinstall does not help, please contact Windscribe support for assistance Exclusive - Исключительный + Исключающий Inclusive - Включительно + Включающий @@ -2369,7 +2369,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Failed - Неудавшийся + Неуспешно @@ -2436,18 +2436,18 @@ If the reinstall does not help, please contact Windscribe support for assistance R.O.B.E.R.T. - Р.О.Б.Е.Р.Т. + R.O.B.E.R.T. Custom - Обычай + Пользовательский OS Default - ОС по умолчанию + Штатный @@ -2479,17 +2479,17 @@ If the reinstall does not help, please contact Windscribe support for assistance Latency - Скрытое состояние + Задержка Bars - Баров + Линии Ms - Госпожа + Миллисекунды @@ -2520,7 +2520,7 @@ If the reinstall does not help, please contact Windscribe support for assistance None - Никакой + Нет @@ -2530,12 +2530,12 @@ If the reinstall does not help, please contact Windscribe support for assistance Release - Отпускать + Релиз Beta - Бета-версия + Бета @@ -2580,27 +2580,27 @@ If the reinstall does not help, please contact Windscribe support for assistance Missing "Interface" section - Отсутствует раздел "Интерфейс" + Отсутствует раздел "Interface" Missing "Peer" section - Отсутствует раздел "Одноранговый узел" + Отсутствует раздел "Peer" Missing "PrivateKey" in the "Interface" section - Отсутствует "PrivateKey" в разделе "Интерфейс" + Отсутствует "PrivateKey" в разделе "Interface" Missing "Address" in the "Interface" section - Отсутствует "Адрес" в разделе "Интерфейс" + Отсутствует "Address" в разделе "Interface" Missing "DNS" in the "Interface" section - Отсутствует "DNS" в разделе "Интерфейс" + Отсутствует "DNS" в разделе "Interface" @@ -2610,7 +2610,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Missing "Endpoint" in the "Peer" section - Отсутствует "Конечная точка" в разделе "Одноранговый узел" + Отсутствует "Endpoint" в разделе "Peer" @@ -2625,7 +2625,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Your application version is no longer supported. Please update to continue using Windscribe. - Версия приложения больше не поддерживается. Пожалуйста, обновите, чтобы продолжить использование Windscribe. + Версия приложения больше не поддерживается. Пожалуйста, обновите приложение для продолжения использования Windscribe. @@ -2635,7 +2635,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Your original account %1 has expired. Creating multiple accounts to bypass free tier limitations is prohibited. Please login into the original account and wait until the bandwidth is reset. You can also upgrade to Pro. - Срок действия вашей первоначальной учетной записи %1 истек. Создание нескольких учетных записей для обхода ограничений уровня бесплатного пользования запрещено. Пожалуйста, войдите в исходную учетную запись и подождите, пока пропускная способность не будет сброшена. Вы также можете перейти на Pro. + Срок действия вашей первоначальной учетной записи %1 истек. Создание нескольких учетных записей для обхода ограничений уровня бесплатного пользования запрещено. Пожалуйста, войдите в исходную учетную запись и подождите, пока пакет трафика не будет сброшен. Вы также можете перейти на Pro. @@ -2722,12 +2722,12 @@ If the reinstall does not help, please contact Windscribe support for assistance Add - Добавлять + Добавить Login - Логин + Войти @@ -2763,7 +2763,7 @@ If the reinstall does not help, please contact Windscribe support for assistance UPDATE - ОБНОВЛЯТЬ + ОБНОВЛЕНИЕ @@ -2776,12 +2776,12 @@ If the reinstall does not help, please contact Windscribe support for assistance You're about to update Windscribe. The application will terminate the active connection and restart automatically. - Вы собираетесь обновить Windscribe. Приложение завершит активное соединение и автоматически перезапустится. + Вы собираетесь обновить Windscribe. Приложение завершит активное подключение и автоматически перезапустится. Your update is in progress, hang in there... - Ваше обновление в процессе, держитесь там... + Ваше обновление в процессе, подождите... @@ -2797,7 +2797,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Update - Обновлять + Обновить @@ -2811,7 +2811,7 @@ If the reinstall does not help, please contact Windscribe support for assistance EXT CONFIG MODE - РЕЖИМ КОНФИГУРАЦИИ EXT + РЕЖИМ СОБСТВЕННОЙ КОНФИГУРАЦИИ @@ -2821,7 +2821,7 @@ If the reinstall does not help, please contact Windscribe support for assistance 1 DAY LEFT - ОСТАЛОСЬ 1 ДЕНЬ + ОСТАЛСЯ 1 ДЕНЬ @@ -2846,7 +2846,7 @@ If the reinstall does not help, please contact Windscribe support for assistance %1 DAYS LEFT - %1 ОСТАЛОСЬ ДНЕЙ + %1 ДНЕЙ ОСТАЛОСЬ @@ -2856,7 +2856,7 @@ If the reinstall does not help, please contact Windscribe support for assistance RENEW - ОБНОВЛЯТЬ + ПРОДЛИТЬ @@ -2864,22 +2864,22 @@ If the reinstall does not help, please contact Windscribe support for assistance You're out of data! - У вас закончились данные! + У вас закончилился пакет трафика! Don't leave your front door open. Upgrade or wait until next month to get your monthly data allowance back. - Не оставляйте входную дверь открытой. Обновите или подождите до следующего месяца, чтобы вернуть ежемесячный объем трафика. + Не оставляйте входную дверь открытой. Оплатите сервис или дождитесь сброса пакета трафика в следующем месяце. Get more data - Получение дополнительных данных + Получить больше трафика I'm broke - Я сломлен + Я на мели @@ -2887,7 +2887,7 @@ If the reinstall does not help, please contact Windscribe support for assistance Best Location - Лучшее расположение + Лучшая локация @@ -2900,22 +2900,22 @@ If the reinstall does not help, please contact Windscribe support for assistance ping new node: %1 (%2 - %3) - проверка связи с новым узлом: %1 (%2 - %3) + ping нового узла: %1 (%2 - %3) ping successful: %1 (%2 - %3) %4ms - Проверка связи выполнена успешно: %1 (%2 - %3) %4ms + ping выполнен успешно: %1 (%2 - %3) %4ms discarding ping while connected: %1 (%2 - %3) - отбрасывание пинга при подключении: %1 (%2 - %3) + игнорирование ping будучи подключённым: %1 (%2 - %3) ping failed: %1 (%2 - %3) - Ошибка проверки связи: %1 (%2 - %3) + ошибка ping: %1 (%2 - %3) diff --git a/installer/linux/arch_package/windscribe.install b/installer/linux/arch_package/windscribe.install index 11d8cde96..e6ea5c970 100644 --- a/installer/linux/arch_package/windscribe.install +++ b/installer/linux/arch_package/windscribe.install @@ -44,7 +44,6 @@ pre_upgrade() { set -e systemctl stop windscribe-helper systemctl disable windscribe-helper - killall -q Windscribe || true userdel -f windscribe || true groupdel -f windscribe || true } @@ -56,7 +55,6 @@ post_upgrade() { pre_remove() { systemctl stop windscribe-helper systemctl disable windscribe-helper - killall -q Windscribe || true userdel -f windscribe || true groupdel -f windscribe || true rm -rf /etc/windscribe diff --git a/installer/linux/common/usr/share/applications/windscribe.desktop b/installer/linux/common/usr/share/applications/windscribe.desktop index ef323de65..3d9066de4 100644 --- a/installer/linux/common/usr/share/applications/windscribe.desktop +++ b/installer/linux/common/usr/share/applications/windscribe.desktop @@ -5,4 +5,3 @@ Exec=/opt/windscribe/Windscribe %F Name=Windscribe Icon=windscribe Categories=Network -StartupWMClass=Windscribe diff --git a/installer/linux/debian_package/DEBIAN/prerm b/installer/linux/debian_package/DEBIAN/prerm index cb23922db..4e7b51bf3 100755 --- a/installer/linux/debian_package/DEBIAN/prerm +++ b/installer/linux/debian_package/DEBIAN/prerm @@ -3,10 +3,8 @@ set -e systemctl stop windscribe-helper systemctl disable windscribe-helper -killall -q Windscribe || true - -deluser --force windscribe || true -delgroup --force windscribe || true +deluser windscribe || true +delgroup windscribe || true rm -f /opt/windscribe/helper_log.txt rm -rf /etc/windscribe diff --git a/installer/linux/rpm_package/SPECS/windscribe_rpm.spec b/installer/linux/rpm_package/SPECS/windscribe_rpm.spec index 4a095bf66..203b89280 100644 --- a/installer/linux/rpm_package/SPECS/windscribe_rpm.spec +++ b/installer/linux/rpm_package/SPECS/windscribe_rpm.spec @@ -59,7 +59,6 @@ echo linux_rpm_x64 > ../etc/windscribe/platform %postun systemctl stop windscribe-helper systemctl disable windscribe-helper -killall -q Windscribe || true userdel -f windscribe || true groupdel -f windscribe || true rm -f /usr/bin/windscribe-cli diff --git a/tools/deps/custom_curl/include/curl/curl.h b/tools/deps/custom_curl/include/curl/curl.h index d522d9473..67e7640ab 100644 --- a/tools/deps/custom_curl/include/curl/curl.h +++ b/tools/deps/custom_curl/include/curl/curl.h @@ -932,6 +932,9 @@ typedef enum { a client certificate for authentication. (Schannel) */ #define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5) +#define CURLSSLOPT_TLSEXT_PADDING (1<<6) +#define CURLSSLOPT_TLSEXT_PADDING_SUPER (1<<7) + /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document this value, keep them in sync. */ diff --git a/tools/deps/custom_curl/lib/setopt.c b/tools/deps/custom_curl/lib/setopt.c index 4eb1ebe3f..ff76db7de 100644 --- a/tools/deps/custom_curl/lib/setopt.c +++ b/tools/deps/custom_curl/lib/setopt.c @@ -2349,6 +2349,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); + data->set.ssl.tlsext_padding = !!(arg & CURLSSLOPT_TLSEXT_PADDING); + data->set.ssl.tlsext_padding_super = !!(arg & CURLSSLOPT_TLSEXT_PADDING_SUPER); /* If a setting is added here it should also be added in dohprobe() which sets its own CURLOPT_SSL_OPTIONS based on these settings. */ break; diff --git a/tools/deps/custom_curl/lib/urldata.h b/tools/deps/custom_curl/lib/urldata.h index f0de1c6d2..35b4cecda 100644 --- a/tools/deps/custom_curl/lib/urldata.h +++ b/tools/deps/custom_curl/lib/urldata.h @@ -314,6 +314,8 @@ struct ssl_config_data { BIT(native_ca_store); /* use the native ca store of operating system */ BIT(auto_client_cert); /* automatically locate and use a client certificate for authentication (Schannel) */ + BIT(tlsext_padding); + BIT(tlsext_padding_super); }; struct ssl_general_config { diff --git a/tools/deps/custom_curl/lib/vtls/openssl.c b/tools/deps/custom_curl/lib/vtls/openssl.c index 3c7adc1ea..c88a7ad3f 100644 --- a/tools/deps/custom_curl/lib/vtls/openssl.c +++ b/tools/deps/custom_curl/lib/vtls/openssl.c @@ -3632,6 +3632,14 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif + if(ssl_config->tlsext_padding) + ctx_options |= SSL_OP_TLSEXT_PADDING; + +#ifdef SSL_OP_TLSEXT_PADDING_SUPER + if(ssl_config->tlsext_padding_super) + ctx_options |= SSL_OP_TLSEXT_PADDING_SUPER; +#endif + switch(ssl_version) { case CURL_SSLVERSION_SSLv2: case CURL_SSLVERSION_SSLv3: diff --git a/tools/deps/custom_openssl_ech_draft/include/openssl/ssl.h.in b/tools/deps/custom_openssl_ech_draft/include/openssl/ssl.h.in new file mode 100644 index 000000000..9ae1f4a29 --- /dev/null +++ b/tools/deps/custom_openssl_ech_draft/include/openssl/ssl.h.in @@ -0,0 +1,2551 @@ +/* + * {- join("\n * ", @autowarntext) -} + * + * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved + * Copyright 2005 Nokia. All rights reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +{- +use OpenSSL::stackhash qw(generate_stack_macros generate_const_stack_macros); +-} + +#ifndef OPENSSL_SSL_H +# define OPENSSL_SSL_H +# pragma once + +# include +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define HEADER_SSL_H +# endif + +# include +# include +# include +# include +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# include +# include +# include +# endif +# include +# include +# include +# include + +# include +# include +# include +# include +# include +# ifndef OPENSSL_NO_STDIO +# include +# endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* OpenSSL version number for ASN.1 encoding of the session information */ +/*- + * Version 0 - initial version + * Version 1 - added the optional peer certificate + */ +# define SSL_SESSION_ASN1_VERSION 0x0001 + +# define SSL_MAX_SSL_SESSION_ID_LENGTH 32 +# define SSL_MAX_SID_CTX_LENGTH 32 + +# define SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES (512/8) +# define SSL_MAX_KEY_ARG_LENGTH 8 +/* SSL_MAX_MASTER_KEY_LENGTH is defined in prov_ssl.h */ + +/* The maximum number of encrypt/decrypt pipelines we can support */ +# define SSL_MAX_PIPELINES 32 + +/* text strings for the ciphers */ + +/* These are used to specify which ciphers to use and not to use */ + +# define SSL_TXT_LOW "LOW" +# define SSL_TXT_MEDIUM "MEDIUM" +# define SSL_TXT_HIGH "HIGH" +# define SSL_TXT_FIPS "FIPS" + +# define SSL_TXT_aNULL "aNULL" +# define SSL_TXT_eNULL "eNULL" +# define SSL_TXT_NULL "NULL" + +# define SSL_TXT_kRSA "kRSA" +# define SSL_TXT_kDHr "kDHr"/* this cipher class has been removed */ +# define SSL_TXT_kDHd "kDHd"/* this cipher class has been removed */ +# define SSL_TXT_kDH "kDH"/* this cipher class has been removed */ +# define SSL_TXT_kEDH "kEDH"/* alias for kDHE */ +# define SSL_TXT_kDHE "kDHE" +# define SSL_TXT_kECDHr "kECDHr"/* this cipher class has been removed */ +# define SSL_TXT_kECDHe "kECDHe"/* this cipher class has been removed */ +# define SSL_TXT_kECDH "kECDH"/* this cipher class has been removed */ +# define SSL_TXT_kEECDH "kEECDH"/* alias for kECDHE */ +# define SSL_TXT_kECDHE "kECDHE" +# define SSL_TXT_kPSK "kPSK" +# define SSL_TXT_kRSAPSK "kRSAPSK" +# define SSL_TXT_kECDHEPSK "kECDHEPSK" +# define SSL_TXT_kDHEPSK "kDHEPSK" +# define SSL_TXT_kGOST "kGOST" +# define SSL_TXT_kGOST18 "kGOST18" +# define SSL_TXT_kSRP "kSRP" + +# define SSL_TXT_aRSA "aRSA" +# define SSL_TXT_aDSS "aDSS" +# define SSL_TXT_aDH "aDH"/* this cipher class has been removed */ +# define SSL_TXT_aECDH "aECDH"/* this cipher class has been removed */ +# define SSL_TXT_aECDSA "aECDSA" +# define SSL_TXT_aPSK "aPSK" +# define SSL_TXT_aGOST94 "aGOST94" +# define SSL_TXT_aGOST01 "aGOST01" +# define SSL_TXT_aGOST12 "aGOST12" +# define SSL_TXT_aGOST "aGOST" +# define SSL_TXT_aSRP "aSRP" + +# define SSL_TXT_DSS "DSS" +# define SSL_TXT_DH "DH" +# define SSL_TXT_DHE "DHE"/* same as "kDHE:-ADH" */ +# define SSL_TXT_EDH "EDH"/* alias for DHE */ +# define SSL_TXT_ADH "ADH" +# define SSL_TXT_RSA "RSA" +# define SSL_TXT_ECDH "ECDH" +# define SSL_TXT_EECDH "EECDH"/* alias for ECDHE" */ +# define SSL_TXT_ECDHE "ECDHE"/* same as "kECDHE:-AECDH" */ +# define SSL_TXT_AECDH "AECDH" +# define SSL_TXT_ECDSA "ECDSA" +# define SSL_TXT_PSK "PSK" +# define SSL_TXT_SRP "SRP" + +# define SSL_TXT_DES "DES" +# define SSL_TXT_3DES "3DES" +# define SSL_TXT_RC4 "RC4" +# define SSL_TXT_RC2 "RC2" +# define SSL_TXT_IDEA "IDEA" +# define SSL_TXT_SEED "SEED" +# define SSL_TXT_AES128 "AES128" +# define SSL_TXT_AES256 "AES256" +# define SSL_TXT_AES "AES" +# define SSL_TXT_AES_GCM "AESGCM" +# define SSL_TXT_AES_CCM "AESCCM" +# define SSL_TXT_AES_CCM_8 "AESCCM8" +# define SSL_TXT_CAMELLIA128 "CAMELLIA128" +# define SSL_TXT_CAMELLIA256 "CAMELLIA256" +# define SSL_TXT_CAMELLIA "CAMELLIA" +# define SSL_TXT_CHACHA20 "CHACHA20" +# define SSL_TXT_GOST "GOST89" +# define SSL_TXT_ARIA "ARIA" +# define SSL_TXT_ARIA_GCM "ARIAGCM" +# define SSL_TXT_ARIA128 "ARIA128" +# define SSL_TXT_ARIA256 "ARIA256" +# define SSL_TXT_GOST2012_GOST8912_GOST8912 "GOST2012-GOST8912-GOST8912" +# define SSL_TXT_CBC "CBC" + +# define SSL_TXT_MD5 "MD5" +# define SSL_TXT_SHA1 "SHA1" +# define SSL_TXT_SHA "SHA"/* same as "SHA1" */ +# define SSL_TXT_GOST94 "GOST94" +# define SSL_TXT_GOST89MAC "GOST89MAC" +# define SSL_TXT_GOST12 "GOST12" +# define SSL_TXT_GOST89MAC12 "GOST89MAC12" +# define SSL_TXT_SHA256 "SHA256" +# define SSL_TXT_SHA384 "SHA384" + +# define SSL_TXT_SSLV3 "SSLv3" +# define SSL_TXT_TLSV1 "TLSv1" +# define SSL_TXT_TLSV1_1 "TLSv1.1" +# define SSL_TXT_TLSV1_2 "TLSv1.2" + +# define SSL_TXT_ALL "ALL" + +/*- + * COMPLEMENTOF* definitions. These identifiers are used to (de-select) + * ciphers normally not being used. + * Example: "RC4" will activate all ciphers using RC4 including ciphers + * without authentication, which would normally disabled by DEFAULT (due + * the "!ADH" being part of default). Therefore "RC4:!COMPLEMENTOFDEFAULT" + * will make sure that it is also disabled in the specific selection. + * COMPLEMENTOF* identifiers are portable between version, as adjustments + * to the default cipher setup will also be included here. + * + * COMPLEMENTOFDEFAULT does not experience the same special treatment that + * DEFAULT gets, as only selection is being done and no sorting as needed + * for DEFAULT. + */ +# define SSL_TXT_CMPALL "COMPLEMENTOFALL" +# define SSL_TXT_CMPDEF "COMPLEMENTOFDEFAULT" + +/* + * The following cipher list is used by default. It also is substituted when + * an application-defined cipher list string starts with 'DEFAULT'. + * This applies to ciphersuites for TLSv1.2 and below. + * DEPRECATED IN 3.0.0, in favor of OSSL_default_cipher_list() + * Update both macro and function simultaneously + */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_DEFAULT_CIPHER_LIST "ALL:!COMPLEMENTOFDEFAULT:!eNULL" +/* + * This is the default set of TLSv1.3 ciphersuites + * DEPRECATED IN 3.0.0, in favor of OSSL_default_ciphersuites() + * Update both macro and function simultaneously + */ +# define TLS_DEFAULT_CIPHERSUITES "TLS_AES_256_GCM_SHA384:" \ + "TLS_CHACHA20_POLY1305_SHA256:" \ + "TLS_AES_128_GCM_SHA256" +# endif +/* + * As of OpenSSL 1.0.0, ssl_create_cipher_list() in ssl/ssl_ciph.c always + * starts with a reasonable order, and all we have to do for DEFAULT is + * throwing out anonymous and unencrypted ciphersuites! (The latter are not + * actually enabled by ALL, but "ALL:RSA" would enable some of them.) + */ + +/* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ +# define SSL_SENT_SHUTDOWN 1 +# define SSL_RECEIVED_SHUTDOWN 2 + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +# define SSL_FILETYPE_ASN1 X509_FILETYPE_ASN1 +# define SSL_FILETYPE_PEM X509_FILETYPE_PEM + +/* + * This is needed to stop compilers complaining about the 'struct ssl_st *' + * function parameters used to prototype callbacks in SSL_CTX. + */ +typedef struct ssl_st *ssl_crock_st; +typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; +typedef struct ssl_method_st SSL_METHOD; +typedef struct ssl_cipher_st SSL_CIPHER; +typedef struct ssl_session_st SSL_SESSION; +typedef struct tls_sigalgs_st TLS_SIGALGS; +typedef struct ssl_conf_ctx_st SSL_CONF_CTX; +typedef struct ssl_comp_st SSL_COMP; + +STACK_OF(SSL_CIPHER); +STACK_OF(SSL_COMP); + +/* SRTP protection profiles for use with the use_srtp extension (RFC 5764)*/ +typedef struct srtp_protection_profile_st { + const char *name; + unsigned long id; +} SRTP_PROTECTION_PROFILE; +{- + generate_stack_macros("SRTP_PROTECTION_PROFILE"); +-} + + +typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, + int len, void *arg); +typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + const SSL_CIPHER **cipher, void *arg); + +/* Extension context codes */ +/* This extension is only allowed in TLS */ +#define SSL_EXT_TLS_ONLY 0x0001 +/* This extension is only allowed in DTLS */ +#define SSL_EXT_DTLS_ONLY 0x0002 +/* Some extensions may be allowed in DTLS but we don't implement them for it */ +#define SSL_EXT_TLS_IMPLEMENTATION_ONLY 0x0004 +/* Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is */ +#define SSL_EXT_SSL3_ALLOWED 0x0008 +/* Extension is only defined for TLS1.2 and below */ +#define SSL_EXT_TLS1_2_AND_BELOW_ONLY 0x0010 +/* Extension is only defined for TLS1.3 and above */ +#define SSL_EXT_TLS1_3_ONLY 0x0020 +/* Ignore this extension during parsing if we are resuming */ +#define SSL_EXT_IGNORE_ON_RESUMPTION 0x0040 +#define SSL_EXT_CLIENT_HELLO 0x0080 +/* Really means TLS1.2 or below */ +#define SSL_EXT_TLS1_2_SERVER_HELLO 0x0100 +#define SSL_EXT_TLS1_3_SERVER_HELLO 0x0200 +#define SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS 0x0400 +#define SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST 0x0800 +#define SSL_EXT_TLS1_3_CERTIFICATE 0x1000 +#define SSL_EXT_TLS1_3_NEW_SESSION_TICKET 0x2000 +#define SSL_EXT_TLS1_3_CERTIFICATE_REQUEST 0x4000 + +/* Typedefs for handling custom extensions */ + +typedef int (*custom_ext_add_cb)(SSL *s, unsigned int ext_type, + const unsigned char **out, size_t *outlen, + int *al, void *add_arg); + +typedef void (*custom_ext_free_cb)(SSL *s, unsigned int ext_type, + const unsigned char *out, void *add_arg); + +typedef int (*custom_ext_parse_cb)(SSL *s, unsigned int ext_type, + const unsigned char *in, size_t inlen, + int *al, void *parse_arg); + + +typedef int (*SSL_custom_ext_add_cb_ex)(SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char **out, + size_t *outlen, X509 *x, + size_t chainidx, + int *al, void *add_arg); + +typedef void (*SSL_custom_ext_free_cb_ex)(SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char *out, + void *add_arg); + +typedef int (*SSL_custom_ext_parse_cb_ex)(SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char *in, + size_t inlen, X509 *x, + size_t chainidx, + int *al, void *parse_arg); + +/* Typedef for verification callback */ +typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx); + +/* Typedef for SSL async callback */ +typedef int (*SSL_async_callback_fn)(SSL *s, void *arg); + +#define SSL_OP_BIT(n) ((uint64_t)1 << (uint64_t)n) + +/* + * SSL/TLS connection options. + */ + /* Disable Extended master secret */ +# define SSL_OP_NO_EXTENDED_MASTER_SECRET SSL_OP_BIT(0) + /* Cleanse plaintext copies of data delivered to the application */ +# define SSL_OP_CLEANSE_PLAINTEXT SSL_OP_BIT(1) + /* Allow initial connection to servers that don't support RI */ +# define SSL_OP_LEGACY_SERVER_CONNECT SSL_OP_BIT(2) + /* Enable support for Kernel TLS */ +# define SSL_OP_ENABLE_KTLS SSL_OP_BIT(3) +# define SSL_OP_TLSEXT_PADDING SSL_OP_BIT(4) +# define SSL_OP_SAFARI_ECDHE_ECDSA_BUG SSL_OP_BIT(6) +# define SSL_OP_IGNORE_UNEXPECTED_EOF SSL_OP_BIT(7) +# define SSL_OP_ALLOW_CLIENT_RENEGOTIATION SSL_OP_BIT(8) +# define SSL_OP_DISABLE_TLSEXT_CA_NAMES SSL_OP_BIT(9) + /* In TLSv1.3 allow a non-(ec)dhe based kex_mode */ +# define SSL_OP_ALLOW_NO_DHE_KEX SSL_OP_BIT(10) + /* + * Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added + * in OpenSSL 0.9.6d. Usually (depending on the application protocol) + * the workaround is not needed. Unfortunately some broken SSL/TLS + * implementations cannot handle it at all, which is why we include it + * in SSL_OP_ALL. Added in 0.9.6e + */ +# define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS SSL_OP_BIT(11) + /* DTLS options */ +# define SSL_OP_NO_QUERY_MTU SSL_OP_BIT(12) + /* Turn on Cookie Exchange (on relevant for servers) */ +# define SSL_OP_COOKIE_EXCHANGE SSL_OP_BIT(13) + /* Don't use RFC4507 ticket extension */ +# define SSL_OP_NO_TICKET SSL_OP_BIT(14) +# ifndef OPENSSL_NO_DTLS1_METHOD + /* + * Use Cisco's version identifier of DTLS_BAD_VER + * (only with deprecated DTLSv1_client_method()) + */ +# define SSL_OP_CISCO_ANYCONNECT SSL_OP_BIT(15) +# endif + /* As server, disallow session resumption on renegotiation */ +# define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION SSL_OP_BIT(16) + /* Don't use compression even if supported */ +# define SSL_OP_NO_COMPRESSION SSL_OP_BIT(17) + /* Permit unsafe legacy renegotiation */ +# define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION SSL_OP_BIT(18) + /* Disable encrypt-then-mac */ +# define SSL_OP_NO_ENCRYPT_THEN_MAC SSL_OP_BIT(19) + /* + * Enable TLSv1.3 Compatibility mode. This is on by default. A future + * version of OpenSSL may have this disabled by default. + */ +# define SSL_OP_ENABLE_MIDDLEBOX_COMPAT SSL_OP_BIT(20) + /* + * Prioritize Chacha20Poly1305 when client does. + * Modifies SSL_OP_CIPHER_SERVER_PREFERENCE + */ +# define SSL_OP_PRIORITIZE_CHACHA SSL_OP_BIT(21) + /* + * Set on servers to choose the cipher according to server's preferences. + */ +# define SSL_OP_CIPHER_SERVER_PREFERENCE SSL_OP_BIT(22) + /* + * If set, a server will allow a client to issue a SSLv3.0 version + * number as latest version supported in the premaster secret, even when + * TLSv1.0 (version 3.1) was announced in the client hello. Normally + * this is forbidden to prevent version rollback attacks. + */ +# define SSL_OP_TLS_ROLLBACK_BUG SSL_OP_BIT(23) + /* + * Switches off automatic TLSv1.3 anti-replay protection for early data. + * This is a server-side option only (no effect on the client). + */ +# define SSL_OP_NO_ANTI_REPLAY SSL_OP_BIT(24) +# define SSL_OP_NO_SSLv3 SSL_OP_BIT(25) +# define SSL_OP_NO_TLSv1 SSL_OP_BIT(26) +# define SSL_OP_NO_TLSv1_2 SSL_OP_BIT(27) +# define SSL_OP_NO_TLSv1_1 SSL_OP_BIT(28) +# define SSL_OP_NO_TLSv1_3 SSL_OP_BIT(29) +# define SSL_OP_NO_DTLSv1 SSL_OP_BIT(26) +# define SSL_OP_NO_DTLSv1_2 SSL_OP_BIT(27) + /* Disallow all renegotiation */ +# define SSL_OP_NO_RENEGOTIATION SSL_OP_BIT(30) + /* + * Make server add server-hello extension from early version of + * cryptopro draft, when GOST ciphersuite is negotiated. Required for + * interoperability with CryptoPro CSP 3.x + */ +# define SSL_OP_CRYPTOPRO_TLSEXT_BUG SSL_OP_BIT(31) + +#ifndef OPENSSL_NO_ECH +/* set this to tell client to emit greased ECH values when not doing + * "real" ECH */ +#define SSL_OP_ECH_GREASE SSL_OP_BIT(32) +/* If this is set then the server side will attempt trial decryption */ +/* of ECHs even if there is no matching record_digest. That's a bit */ +/* inefficient, but more privacy friendly */ +#define SSL_OP_ECH_TRIALDECRYPT SSL_OP_BIT(33) +/* If set, clients will ignore the supplied ECH config_id and replace + * that with a random value */ +#define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(34) +#endif +/* Super large padding to bypass non-reassembling censorship filters */ +#define SSL_OP_TLSEXT_PADDING_SUPER SSL_OP_BIT(35) + +/* + * Option "collections." + */ +# define SSL_OP_NO_SSL_MASK \ + ( SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 \ + | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3 ) +# define SSL_OP_NO_DTLS_MASK \ + ( SSL_OP_NO_DTLSv1 | SSL_OP_NO_DTLSv1_2 ) + +/* Various bug workarounds that should be rather harmless. */ +# define SSL_OP_ALL \ + ( SSL_OP_CRYPTOPRO_TLSEXT_BUG | SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS \ + | SSL_OP_TLSEXT_PADDING | SSL_OP_SAFARI_ECDHE_ECDSA_BUG ) + +/* + * OBSOLETE OPTIONS retained for compatibility + */ + +# define SSL_OP_MICROSOFT_SESS_ID_BUG 0x0 +# define SSL_OP_NETSCAPE_CHALLENGE_BUG 0x0 +# define SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG 0x0 +# define SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG 0x0 +# define SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER 0x0 +# define SSL_OP_MSIE_SSLV2_RSA_PADDING 0x0 +# define SSL_OP_SSLEAY_080_CLIENT_DH_BUG 0x0 +# define SSL_OP_TLS_D5_BUG 0x0 +# define SSL_OP_TLS_BLOCK_PADDING_BUG 0x0 +# define SSL_OP_SINGLE_ECDH_USE 0x0 +# define SSL_OP_SINGLE_DH_USE 0x0 +# define SSL_OP_EPHEMERAL_RSA 0x0 +# define SSL_OP_NO_SSLv2 0x0 +# define SSL_OP_PKCS1_CHECK_1 0x0 +# define SSL_OP_PKCS1_CHECK_2 0x0 +# define SSL_OP_NETSCAPE_CA_DN_BUG 0x0 +# define SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG 0x0 + +/* + * Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success + * when just a single record has been written): + */ +# define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001U +/* + * Make it possible to retry SSL_write() with changed buffer location (buffer + * contents must stay the same!); this is not the default to avoid the + * misconception that non-blocking SSL_write() behaves like non-blocking + * write(): + */ +# define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002U +/* + * Never bother the application with retries if the transport is blocking: + */ +# define SSL_MODE_AUTO_RETRY 0x00000004U +/* Don't attempt to automatically build certificate chain */ +# define SSL_MODE_NO_AUTO_CHAIN 0x00000008U +/* + * Save RAM by releasing read and write buffers when they're empty. (SSL3 and + * TLS only.) Released buffers are freed. + */ +# define SSL_MODE_RELEASE_BUFFERS 0x00000010U +/* + * Send the current time in the Random fields of the ClientHello and + * ServerHello records for compatibility with hypothetical implementations + * that require it. + */ +# define SSL_MODE_SEND_CLIENTHELLO_TIME 0x00000020U +# define SSL_MODE_SEND_SERVERHELLO_TIME 0x00000040U +/* + * Send TLS_FALLBACK_SCSV in the ClientHello. To be set only by applications + * that reconnect with a downgraded protocol version; see + * draft-ietf-tls-downgrade-scsv-00 for details. DO NOT ENABLE THIS if your + * application attempts a normal handshake. Only use this in explicit + * fallback retries, following the guidance in + * draft-ietf-tls-downgrade-scsv-00. + */ +# define SSL_MODE_SEND_FALLBACK_SCSV 0x00000080U +/* + * Support Asynchronous operation + */ +# define SSL_MODE_ASYNC 0x00000100U + +/* + * When using DTLS/SCTP, include the terminating zero in the label + * used for computing the endpoint-pair shared secret. Required for + * interoperability with implementations having this bug like these + * older version of OpenSSL: + * - OpenSSL 1.0.0 series + * - OpenSSL 1.0.1 series + * - OpenSSL 1.0.2 series + * - OpenSSL 1.1.0 series + * - OpenSSL 1.1.1 and 1.1.1a + */ +# define SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG 0x00000400U + +/* Cert related flags */ +/* + * Many implementations ignore some aspects of the TLS standards such as + * enforcing certificate chain algorithms. When this is set we enforce them. + */ +# define SSL_CERT_FLAG_TLS_STRICT 0x00000001U + +/* Suite B modes, takes same values as certificate verify flags */ +# define SSL_CERT_FLAG_SUITEB_128_LOS_ONLY 0x10000 +/* Suite B 192 bit only mode */ +# define SSL_CERT_FLAG_SUITEB_192_LOS 0x20000 +/* Suite B 128 bit mode allowing 192 bit algorithms */ +# define SSL_CERT_FLAG_SUITEB_128_LOS 0x30000 + +/* Perform all sorts of protocol violations for testing purposes */ +# define SSL_CERT_FLAG_BROKEN_PROTOCOL 0x10000000 + +/* Flags for building certificate chains */ +/* Treat any existing certificates as untrusted CAs */ +# define SSL_BUILD_CHAIN_FLAG_UNTRUSTED 0x1 +/* Don't include root CA in chain */ +# define SSL_BUILD_CHAIN_FLAG_NO_ROOT 0x2 +/* Just check certificates already there */ +# define SSL_BUILD_CHAIN_FLAG_CHECK 0x4 +/* Ignore verification errors */ +# define SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR 0x8 +/* Clear verification errors from queue */ +# define SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR 0x10 + +/* Flags returned by SSL_check_chain */ +/* Certificate can be used with this session */ +# define CERT_PKEY_VALID 0x1 +/* Certificate can also be used for signing */ +# define CERT_PKEY_SIGN 0x2 +/* EE certificate signing algorithm OK */ +# define CERT_PKEY_EE_SIGNATURE 0x10 +/* CA signature algorithms OK */ +# define CERT_PKEY_CA_SIGNATURE 0x20 +/* EE certificate parameters OK */ +# define CERT_PKEY_EE_PARAM 0x40 +/* CA certificate parameters OK */ +# define CERT_PKEY_CA_PARAM 0x80 +/* Signing explicitly allowed as opposed to SHA1 fallback */ +# define CERT_PKEY_EXPLICIT_SIGN 0x100 +/* Client CA issuer names match (always set for server cert) */ +# define CERT_PKEY_ISSUER_NAME 0x200 +/* Cert type matches client types (always set for server cert) */ +# define CERT_PKEY_CERT_TYPE 0x400 +/* Cert chain suitable to Suite B */ +# define CERT_PKEY_SUITEB 0x800 + +# define SSL_CONF_FLAG_CMDLINE 0x1 +# define SSL_CONF_FLAG_FILE 0x2 +# define SSL_CONF_FLAG_CLIENT 0x4 +# define SSL_CONF_FLAG_SERVER 0x8 +# define SSL_CONF_FLAG_SHOW_ERRORS 0x10 +# define SSL_CONF_FLAG_CERTIFICATE 0x20 +# define SSL_CONF_FLAG_REQUIRE_PRIVATE 0x40 +/* Configuration value types */ +# define SSL_CONF_TYPE_UNKNOWN 0x0 +# define SSL_CONF_TYPE_STRING 0x1 +# define SSL_CONF_TYPE_FILE 0x2 +# define SSL_CONF_TYPE_DIR 0x3 +# define SSL_CONF_TYPE_NONE 0x4 +# define SSL_CONF_TYPE_STORE 0x5 + +/* Maximum length of the application-controlled segment of a a TLSv1.3 cookie */ +# define SSL_COOKIE_LENGTH 4096 + +/* + * Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value, they + * cannot be used to clear bits. + */ + +uint64_t SSL_CTX_get_options(const SSL_CTX *ctx); +uint64_t SSL_get_options(const SSL *s); +uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op); +uint64_t SSL_clear_options(SSL *s, uint64_t op); +uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op); +uint64_t SSL_set_options(SSL *s, uint64_t op); + +# define SSL_CTX_set_mode(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL) +# define SSL_CTX_clear_mode(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_CLEAR_MODE,(op),NULL) +# define SSL_CTX_get_mode(ctx) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,0,NULL) +# define SSL_clear_mode(ssl,op) \ + SSL_ctrl((ssl),SSL_CTRL_CLEAR_MODE,(op),NULL) +# define SSL_set_mode(ssl,op) \ + SSL_ctrl((ssl),SSL_CTRL_MODE,(op),NULL) +# define SSL_get_mode(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_MODE,0,NULL) +# define SSL_set_mtu(ssl, mtu) \ + SSL_ctrl((ssl),SSL_CTRL_SET_MTU,(mtu),NULL) +# define DTLS_set_link_mtu(ssl, mtu) \ + SSL_ctrl((ssl),DTLS_CTRL_SET_LINK_MTU,(mtu),NULL) +# define DTLS_get_link_min_mtu(ssl) \ + SSL_ctrl((ssl),DTLS_CTRL_GET_LINK_MIN_MTU,0,NULL) + +# define SSL_get_secure_renegotiation_support(ssl) \ + SSL_ctrl((ssl), SSL_CTRL_GET_RI_SUPPORT, 0, NULL) + +# define SSL_CTX_set_cert_flags(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_CERT_FLAGS,(op),NULL) +# define SSL_set_cert_flags(s,op) \ + SSL_ctrl((s),SSL_CTRL_CERT_FLAGS,(op),NULL) +# define SSL_CTX_clear_cert_flags(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_CLEAR_CERT_FLAGS,(op),NULL) +# define SSL_clear_cert_flags(s,op) \ + SSL_ctrl((s),SSL_CTRL_CLEAR_CERT_FLAGS,(op),NULL) + +void SSL_CTX_set_msg_callback(SSL_CTX *ctx, + void (*cb) (int write_p, int version, + int content_type, const void *buf, + size_t len, SSL *ssl, void *arg)); +void SSL_set_msg_callback(SSL *ssl, + void (*cb) (int write_p, int version, + int content_type, const void *buf, + size_t len, SSL *ssl, void *arg)); +# define SSL_CTX_set_msg_callback_arg(ctx, arg) SSL_CTX_ctrl((ctx), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) +# define SSL_set_msg_callback_arg(ssl, arg) SSL_ctrl((ssl), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) + +# define SSL_get_extms_support(s) \ + SSL_ctrl((s),SSL_CTRL_GET_EXTMS_SUPPORT,0,NULL) + +# ifndef OPENSSL_NO_SRP +/* see tls_srp.c */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 __owur int SSL_SRP_CTX_init(SSL *s); +OSSL_DEPRECATEDIN_3_0 __owur int SSL_CTX_SRP_CTX_init(SSL_CTX *ctx); +OSSL_DEPRECATEDIN_3_0 int SSL_SRP_CTX_free(SSL *ctx); +OSSL_DEPRECATEDIN_3_0 int SSL_CTX_SRP_CTX_free(SSL_CTX *ctx); +OSSL_DEPRECATEDIN_3_0 __owur int SSL_srp_server_param_with_username(SSL *s, + int *ad); +OSSL_DEPRECATEDIN_3_0 __owur int SRP_Calc_A_param(SSL *s); +# endif +# endif + +/* 100k max cert list */ +# define SSL_MAX_CERT_LIST_DEFAULT (1024*100) + +# define SSL_SESSION_CACHE_MAX_SIZE_DEFAULT (1024*20) + +/* + * This callback type is used inside SSL_CTX, SSL, and in the functions that + * set them. It is used to override the generation of SSL/TLS session IDs in + * a server. Return value should be zero on an error, non-zero to proceed. + * Also, callbacks should themselves check if the id they generate is unique + * otherwise the SSL handshake will fail with an error - callbacks can do + * this using the 'ssl' value they're passed by; + * SSL_has_matching_session_id(ssl, id, *id_len) The length value passed in + * is set at the maximum size the session ID can be. In SSLv3/TLSv1 it is 32 + * bytes. The callback can alter this length to be less if desired. It is + * also an error for the callback to set the size to zero. + */ +typedef int (*GEN_SESSION_CB) (SSL *ssl, unsigned char *id, + unsigned int *id_len); + +# define SSL_SESS_CACHE_OFF 0x0000 +# define SSL_SESS_CACHE_CLIENT 0x0001 +# define SSL_SESS_CACHE_SERVER 0x0002 +# define SSL_SESS_CACHE_BOTH (SSL_SESS_CACHE_CLIENT|SSL_SESS_CACHE_SERVER) +# define SSL_SESS_CACHE_NO_AUTO_CLEAR 0x0080 +/* enough comments already ... see SSL_CTX_set_session_cache_mode(3) */ +# define SSL_SESS_CACHE_NO_INTERNAL_LOOKUP 0x0100 +# define SSL_SESS_CACHE_NO_INTERNAL_STORE 0x0200 +# define SSL_SESS_CACHE_NO_INTERNAL \ + (SSL_SESS_CACHE_NO_INTERNAL_LOOKUP|SSL_SESS_CACHE_NO_INTERNAL_STORE) +# define SSL_SESS_CACHE_UPDATE_TIME 0x0400 + +LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx); +# define SSL_CTX_sess_number(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_NUMBER,0,NULL) +# define SSL_CTX_sess_connect(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT,0,NULL) +# define SSL_CTX_sess_connect_good(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT_GOOD,0,NULL) +# define SSL_CTX_sess_connect_renegotiate(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT_RENEGOTIATE,0,NULL) +# define SSL_CTX_sess_accept(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT,0,NULL) +# define SSL_CTX_sess_accept_renegotiate(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT_RENEGOTIATE,0,NULL) +# define SSL_CTX_sess_accept_good(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT_GOOD,0,NULL) +# define SSL_CTX_sess_hits(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_HIT,0,NULL) +# define SSL_CTX_sess_cb_hits(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CB_HIT,0,NULL) +# define SSL_CTX_sess_misses(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_MISSES,0,NULL) +# define SSL_CTX_sess_timeouts(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_TIMEOUTS,0,NULL) +# define SSL_CTX_sess_cache_full(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CACHE_FULL,0,NULL) + +void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, + int (*new_session_cb) (struct ssl_st *ssl, + SSL_SESSION *sess)); +int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx)) (struct ssl_st *ssl, + SSL_SESSION *sess); +void SSL_CTX_sess_set_remove_cb(SSL_CTX *ctx, + void (*remove_session_cb) (struct ssl_ctx_st + *ctx, + SSL_SESSION *sess)); +void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx)) (struct ssl_ctx_st *ctx, + SSL_SESSION *sess); +void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx, + SSL_SESSION *(*get_session_cb) (struct ssl_st + *ssl, + const unsigned char + *data, int len, + int *copy)); +SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx)) (struct ssl_st *ssl, + const unsigned char *data, + int len, int *copy); +void SSL_CTX_set_info_callback(SSL_CTX *ctx, + void (*cb) (const SSL *ssl, int type, int val)); +void (*SSL_CTX_get_info_callback(SSL_CTX *ctx)) (const SSL *ssl, int type, + int val); +void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, + int (*client_cert_cb) (SSL *ssl, X509 **x509, + EVP_PKEY **pkey)); +int (*SSL_CTX_get_client_cert_cb(SSL_CTX *ctx)) (SSL *ssl, X509 **x509, + EVP_PKEY **pkey); +# ifndef OPENSSL_NO_ENGINE +__owur int SSL_CTX_set_client_cert_engine(SSL_CTX *ctx, ENGINE *e); +# endif +void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, + int (*app_gen_cookie_cb) (SSL *ssl, + unsigned char + *cookie, + unsigned int + *cookie_len)); +void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, + int (*app_verify_cookie_cb) (SSL *ssl, + const unsigned + char *cookie, + unsigned int + cookie_len)); + +void SSL_CTX_set_stateless_cookie_generate_cb( + SSL_CTX *ctx, + int (*gen_stateless_cookie_cb) (SSL *ssl, + unsigned char *cookie, + size_t *cookie_len)); +void SSL_CTX_set_stateless_cookie_verify_cb( + SSL_CTX *ctx, + int (*verify_stateless_cookie_cb) (SSL *ssl, + const unsigned char *cookie, + size_t cookie_len)); +# ifndef OPENSSL_NO_NEXTPROTONEG + +typedef int (*SSL_CTX_npn_advertised_cb_func)(SSL *ssl, + const unsigned char **out, + unsigned int *outlen, + void *arg); +void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *s, + SSL_CTX_npn_advertised_cb_func cb, + void *arg); +# define SSL_CTX_set_npn_advertised_cb SSL_CTX_set_next_protos_advertised_cb + +typedef int (*SSL_CTX_npn_select_cb_func)(SSL *s, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg); +void SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, + SSL_CTX_npn_select_cb_func cb, + void *arg); +# define SSL_CTX_set_npn_select_cb SSL_CTX_set_next_proto_select_cb + +void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, + unsigned *len); +# define SSL_get0_npn_negotiated SSL_get0_next_proto_negotiated +# endif + +__owur int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const unsigned char *client, + unsigned int client_len); + +# define OPENSSL_NPN_UNSUPPORTED 0 +# define OPENSSL_NPN_NEGOTIATED 1 +# define OPENSSL_NPN_NO_OVERLAP 2 + +__owur int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, + unsigned int protos_len); +__owur int SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos, + unsigned int protos_len); +typedef int (*SSL_CTX_alpn_select_cb_func)(SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg); +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + SSL_CTX_alpn_select_cb_func cb, + void *arg); +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned int *len); + +# ifndef OPENSSL_NO_PSK +/* + * the maximum length of the buffer given to callbacks containing the + * resulting identity/psk + */ +# define PSK_MAX_IDENTITY_LEN 256 +# define PSK_MAX_PSK_LEN 512 +typedef unsigned int (*SSL_psk_client_cb_func)(SSL *ssl, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len); +void SSL_CTX_set_psk_client_callback(SSL_CTX *ctx, SSL_psk_client_cb_func cb); +void SSL_set_psk_client_callback(SSL *ssl, SSL_psk_client_cb_func cb); + +typedef unsigned int (*SSL_psk_server_cb_func)(SSL *ssl, + const char *identity, + unsigned char *psk, + unsigned int max_psk_len); +void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, SSL_psk_server_cb_func cb); +void SSL_set_psk_server_callback(SSL *ssl, SSL_psk_server_cb_func cb); + +__owur int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint); +__owur int SSL_use_psk_identity_hint(SSL *s, const char *identity_hint); +const char *SSL_get_psk_identity_hint(const SSL *s); +const char *SSL_get_psk_identity(const SSL *s); +# endif + +typedef int (*SSL_psk_find_session_cb_func)(SSL *ssl, + const unsigned char *identity, + size_t identity_len, + SSL_SESSION **sess); +typedef int (*SSL_psk_use_session_cb_func)(SSL *ssl, const EVP_MD *md, + const unsigned char **id, + size_t *idlen, + SSL_SESSION **sess); + +void SSL_set_psk_find_session_callback(SSL *s, SSL_psk_find_session_cb_func cb); +void SSL_CTX_set_psk_find_session_callback(SSL_CTX *ctx, + SSL_psk_find_session_cb_func cb); +void SSL_set_psk_use_session_callback(SSL *s, SSL_psk_use_session_cb_func cb); +void SSL_CTX_set_psk_use_session_callback(SSL_CTX *ctx, + SSL_psk_use_session_cb_func cb); + +/* Register callbacks to handle custom TLS Extensions for client or server. */ + +__owur int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, + unsigned int ext_type); + +__owur int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, + unsigned int ext_type, + custom_ext_add_cb add_cb, + custom_ext_free_cb free_cb, + void *add_arg, + custom_ext_parse_cb parse_cb, + void *parse_arg); + +__owur int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, + unsigned int ext_type, + custom_ext_add_cb add_cb, + custom_ext_free_cb free_cb, + void *add_arg, + custom_ext_parse_cb parse_cb, + void *parse_arg); + +__owur int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type, + unsigned int context, + SSL_custom_ext_add_cb_ex add_cb, + SSL_custom_ext_free_cb_ex free_cb, + void *add_arg, + SSL_custom_ext_parse_cb_ex parse_cb, + void *parse_arg); + +__owur int SSL_extension_supported(unsigned int ext_type); + +# define SSL_NOTHING 1 +# define SSL_WRITING 2 +# define SSL_READING 3 +# define SSL_X509_LOOKUP 4 +# define SSL_ASYNC_PAUSED 5 +# define SSL_ASYNC_NO_JOBS 6 +# define SSL_CLIENT_HELLO_CB 7 +# define SSL_RETRY_VERIFY 8 + +/* These will only be used when doing non-blocking IO */ +# define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) +# define SSL_want_read(s) (SSL_want(s) == SSL_READING) +# define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) +# define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP) +# define SSL_want_retry_verify(s) (SSL_want(s) == SSL_RETRY_VERIFY) +# define SSL_want_async(s) (SSL_want(s) == SSL_ASYNC_PAUSED) +# define SSL_want_async_job(s) (SSL_want(s) == SSL_ASYNC_NO_JOBS) +# define SSL_want_client_hello_cb(s) (SSL_want(s) == SSL_CLIENT_HELLO_CB) + +# define SSL_MAC_FLAG_READ_MAC_STREAM 1 +# define SSL_MAC_FLAG_WRITE_MAC_STREAM 2 +# define SSL_MAC_FLAG_READ_MAC_TLSTREE 4 +# define SSL_MAC_FLAG_WRITE_MAC_TLSTREE 8 + +/* + * A callback for logging out TLS key material. This callback should log out + * |line| followed by a newline. + */ +typedef void (*SSL_CTX_keylog_cb_func)(const SSL *ssl, const char *line); + +/* + * SSL_CTX_set_keylog_callback configures a callback to log key material. This + * is intended for debugging use with tools like Wireshark. The cb function + * should log line followed by a newline. + */ +void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb); + +/* + * SSL_CTX_get_keylog_callback returns the callback configured by + * SSL_CTX_set_keylog_callback. + */ +SSL_CTX_keylog_cb_func SSL_CTX_get_keylog_callback(const SSL_CTX *ctx); + +int SSL_CTX_set_max_early_data(SSL_CTX *ctx, uint32_t max_early_data); +uint32_t SSL_CTX_get_max_early_data(const SSL_CTX *ctx); +int SSL_set_max_early_data(SSL *s, uint32_t max_early_data); +uint32_t SSL_get_max_early_data(const SSL *s); +int SSL_CTX_set_recv_max_early_data(SSL_CTX *ctx, uint32_t recv_max_early_data); +uint32_t SSL_CTX_get_recv_max_early_data(const SSL_CTX *ctx); +int SSL_set_recv_max_early_data(SSL *s, uint32_t recv_max_early_data); +uint32_t SSL_get_recv_max_early_data(const SSL *s); + +#ifdef __cplusplus +} +#endif + +# include +# include +# include /* This is mostly sslv3 with a few tweaks */ +# include /* Datagram TLS */ +# include /* Support for the use_srtp extension */ +# include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * These need to be after the above set of includes due to a compiler bug + * in VisualStudio 2015 + */ +{- + generate_const_stack_macros("SSL_CIPHER") + .generate_stack_macros("SSL_COMP"); +-} + +/* compatibility */ +# define SSL_set_app_data(s,arg) (SSL_set_ex_data(s,0,(char *)(arg))) +# define SSL_get_app_data(s) (SSL_get_ex_data(s,0)) +# define SSL_SESSION_set_app_data(s,a) (SSL_SESSION_set_ex_data(s,0, \ + (char *)(a))) +# define SSL_SESSION_get_app_data(s) (SSL_SESSION_get_ex_data(s,0)) +# define SSL_CTX_get_app_data(ctx) (SSL_CTX_get_ex_data(ctx,0)) +# define SSL_CTX_set_app_data(ctx,arg) (SSL_CTX_set_ex_data(ctx,0, \ + (char *)(arg))) +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 void SSL_set_debug(SSL *s, int debug); +# endif + +/* TLSv1.3 KeyUpdate message types */ +/* -1 used so that this is an invalid value for the on-the-wire protocol */ +#define SSL_KEY_UPDATE_NONE -1 +/* Values as defined for the on-the-wire protocol */ +#define SSL_KEY_UPDATE_NOT_REQUESTED 0 +#define SSL_KEY_UPDATE_REQUESTED 1 + +/* + * The valid handshake states (one for each type message sent and one for each + * type of message received). There are also two "special" states: + * TLS = TLS or DTLS state + * DTLS = DTLS specific state + * CR/SR = Client Read/Server Read + * CW/SW = Client Write/Server Write + * + * The "special" states are: + * TLS_ST_BEFORE = No handshake has been initiated yet + * TLS_ST_OK = A handshake has been successfully completed + */ +typedef enum { + TLS_ST_BEFORE, + TLS_ST_OK, + DTLS_ST_CR_HELLO_VERIFY_REQUEST, + TLS_ST_CR_SRVR_HELLO, + TLS_ST_CR_CERT, + TLS_ST_CR_CERT_STATUS, + TLS_ST_CR_KEY_EXCH, + TLS_ST_CR_CERT_REQ, + TLS_ST_CR_SRVR_DONE, + TLS_ST_CR_SESSION_TICKET, + TLS_ST_CR_CHANGE, + TLS_ST_CR_FINISHED, + TLS_ST_CW_CLNT_HELLO, + TLS_ST_CW_CERT, + TLS_ST_CW_KEY_EXCH, + TLS_ST_CW_CERT_VRFY, + TLS_ST_CW_CHANGE, + TLS_ST_CW_NEXT_PROTO, + TLS_ST_CW_FINISHED, + TLS_ST_SW_HELLO_REQ, + TLS_ST_SR_CLNT_HELLO, + DTLS_ST_SW_HELLO_VERIFY_REQUEST, + TLS_ST_SW_SRVR_HELLO, + TLS_ST_SW_CERT, + TLS_ST_SW_KEY_EXCH, + TLS_ST_SW_CERT_REQ, + TLS_ST_SW_SRVR_DONE, + TLS_ST_SR_CERT, + TLS_ST_SR_KEY_EXCH, + TLS_ST_SR_CERT_VRFY, + TLS_ST_SR_NEXT_PROTO, + TLS_ST_SR_CHANGE, + TLS_ST_SR_FINISHED, + TLS_ST_SW_SESSION_TICKET, + TLS_ST_SW_CERT_STATUS, + TLS_ST_SW_CHANGE, + TLS_ST_SW_FINISHED, + TLS_ST_SW_ENCRYPTED_EXTENSIONS, + TLS_ST_CR_ENCRYPTED_EXTENSIONS, + TLS_ST_CR_CERT_VRFY, + TLS_ST_SW_CERT_VRFY, + TLS_ST_CR_HELLO_REQ, + TLS_ST_SW_KEY_UPDATE, + TLS_ST_CW_KEY_UPDATE, + TLS_ST_SR_KEY_UPDATE, + TLS_ST_CR_KEY_UPDATE, + TLS_ST_EARLY_DATA, + TLS_ST_PENDING_EARLY_DATA_END, + TLS_ST_CW_END_OF_EARLY_DATA, + TLS_ST_SR_END_OF_EARLY_DATA +} OSSL_HANDSHAKE_STATE; + +/* + * Most of the following state values are no longer used and are defined to be + * the closest equivalent value in the current state machine code. Not all + * defines have an equivalent and are set to a dummy value (-1). SSL_ST_CONNECT + * and SSL_ST_ACCEPT are still in use in the definition of SSL_CB_ACCEPT_LOOP, + * SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP and SSL_CB_CONNECT_EXIT. + */ + +# define SSL_ST_CONNECT 0x1000 +# define SSL_ST_ACCEPT 0x2000 + +# define SSL_ST_MASK 0x0FFF + +# define SSL_CB_LOOP 0x01 +# define SSL_CB_EXIT 0x02 +# define SSL_CB_READ 0x04 +# define SSL_CB_WRITE 0x08 +# define SSL_CB_ALERT 0x4000/* used in callback */ +# define SSL_CB_READ_ALERT (SSL_CB_ALERT|SSL_CB_READ) +# define SSL_CB_WRITE_ALERT (SSL_CB_ALERT|SSL_CB_WRITE) +# define SSL_CB_ACCEPT_LOOP (SSL_ST_ACCEPT|SSL_CB_LOOP) +# define SSL_CB_ACCEPT_EXIT (SSL_ST_ACCEPT|SSL_CB_EXIT) +# define SSL_CB_CONNECT_LOOP (SSL_ST_CONNECT|SSL_CB_LOOP) +# define SSL_CB_CONNECT_EXIT (SSL_ST_CONNECT|SSL_CB_EXIT) +# define SSL_CB_HANDSHAKE_START 0x10 +# define SSL_CB_HANDSHAKE_DONE 0x20 + +/* Is the SSL_connection established? */ +# define SSL_in_connect_init(a) (SSL_in_init(a) && !SSL_is_server(a)) +# define SSL_in_accept_init(a) (SSL_in_init(a) && SSL_is_server(a)) +int SSL_in_init(const SSL *s); +int SSL_in_before(const SSL *s); +int SSL_is_init_finished(const SSL *s); + +/* + * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you + * should not need these + */ +# define SSL_ST_READ_HEADER 0xF0 +# define SSL_ST_READ_BODY 0xF1 +# define SSL_ST_READ_DONE 0xF2 + +/*- + * Obtain latest Finished message + * -- that we sent (SSL_get_finished) + * -- that we expected from peer (SSL_get_peer_finished). + * Returns length (0 == no Finished so far), copies up to 'count' bytes. + */ +size_t SSL_get_finished(const SSL *s, void *buf, size_t count); +size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); + +/* + * use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 3 options are + * 'ored' with SSL_VERIFY_PEER if they are desired + */ +# define SSL_VERIFY_NONE 0x00 +# define SSL_VERIFY_PEER 0x01 +# define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +# define SSL_VERIFY_CLIENT_ONCE 0x04 +# define SSL_VERIFY_POST_HANDSHAKE 0x08 + +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# define OpenSSL_add_ssl_algorithms() SSL_library_init() +# define SSLeay_add_ssl_algorithms() SSL_library_init() +# endif + +/* More backward compatibility */ +# define SSL_get_cipher(s) \ + SSL_CIPHER_get_name(SSL_get_current_cipher(s)) +# define SSL_get_cipher_bits(s,np) \ + SSL_CIPHER_get_bits(SSL_get_current_cipher(s),np) +# define SSL_get_cipher_version(s) \ + SSL_CIPHER_get_version(SSL_get_current_cipher(s)) +# define SSL_get_cipher_name(s) \ + SSL_CIPHER_get_name(SSL_get_current_cipher(s)) +# define SSL_get_time(a) SSL_SESSION_get_time(a) +# define SSL_set_time(a,b) SSL_SESSION_set_time((a),(b)) +# define SSL_get_timeout(a) SSL_SESSION_get_timeout(a) +# define SSL_set_timeout(a,b) SSL_SESSION_set_timeout((a),(b)) + +# define d2i_SSL_SESSION_bio(bp,s_id) ASN1_d2i_bio_of(SSL_SESSION,SSL_SESSION_new,d2i_SSL_SESSION,bp,s_id) +# define i2d_SSL_SESSION_bio(bp,s_id) ASN1_i2d_bio_of(SSL_SESSION,i2d_SSL_SESSION,bp,s_id) + +DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) +# define SSL_AD_REASON_OFFSET 1000/* offset to get SSL_R_... value + * from SSL_AD_... */ +/* These alert types are for SSLv3 and TLSv1 */ +# define SSL_AD_CLOSE_NOTIFY SSL3_AD_CLOSE_NOTIFY +/* fatal */ +# define SSL_AD_UNEXPECTED_MESSAGE SSL3_AD_UNEXPECTED_MESSAGE +/* fatal */ +# define SSL_AD_BAD_RECORD_MAC SSL3_AD_BAD_RECORD_MAC +# define SSL_AD_DECRYPTION_FAILED TLS1_AD_DECRYPTION_FAILED +# define SSL_AD_RECORD_OVERFLOW TLS1_AD_RECORD_OVERFLOW +/* fatal */ +# define SSL_AD_DECOMPRESSION_FAILURE SSL3_AD_DECOMPRESSION_FAILURE +/* fatal */ +# define SSL_AD_HANDSHAKE_FAILURE SSL3_AD_HANDSHAKE_FAILURE +/* Not for TLS */ +# define SSL_AD_NO_CERTIFICATE SSL3_AD_NO_CERTIFICATE +# define SSL_AD_BAD_CERTIFICATE SSL3_AD_BAD_CERTIFICATE +# define SSL_AD_UNSUPPORTED_CERTIFICATE SSL3_AD_UNSUPPORTED_CERTIFICATE +# define SSL_AD_CERTIFICATE_REVOKED SSL3_AD_CERTIFICATE_REVOKED +# define SSL_AD_CERTIFICATE_EXPIRED SSL3_AD_CERTIFICATE_EXPIRED +# define SSL_AD_CERTIFICATE_UNKNOWN SSL3_AD_CERTIFICATE_UNKNOWN +/* fatal */ +# define SSL_AD_ILLEGAL_PARAMETER SSL3_AD_ILLEGAL_PARAMETER +/* fatal */ +# define SSL_AD_UNKNOWN_CA TLS1_AD_UNKNOWN_CA +/* fatal */ +# define SSL_AD_ACCESS_DENIED TLS1_AD_ACCESS_DENIED +/* fatal */ +# define SSL_AD_DECODE_ERROR TLS1_AD_DECODE_ERROR +# define SSL_AD_DECRYPT_ERROR TLS1_AD_DECRYPT_ERROR +/* fatal */ +# define SSL_AD_EXPORT_RESTRICTION TLS1_AD_EXPORT_RESTRICTION +/* fatal */ +# define SSL_AD_PROTOCOL_VERSION TLS1_AD_PROTOCOL_VERSION +/* fatal */ +# define SSL_AD_INSUFFICIENT_SECURITY TLS1_AD_INSUFFICIENT_SECURITY +/* fatal */ +# define SSL_AD_INTERNAL_ERROR TLS1_AD_INTERNAL_ERROR +# define SSL_AD_USER_CANCELLED TLS1_AD_USER_CANCELLED +# define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION +# define SSL_AD_MISSING_EXTENSION TLS13_AD_MISSING_EXTENSION +# define SSL_AD_CERTIFICATE_REQUIRED TLS13_AD_CERTIFICATE_REQUIRED +# define SSL_AD_UNSUPPORTED_EXTENSION TLS1_AD_UNSUPPORTED_EXTENSION +# define SSL_AD_CERTIFICATE_UNOBTAINABLE TLS1_AD_CERTIFICATE_UNOBTAINABLE +# define SSL_AD_UNRECOGNIZED_NAME TLS1_AD_UNRECOGNIZED_NAME +# define SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE +# define SSL_AD_BAD_CERTIFICATE_HASH_VALUE TLS1_AD_BAD_CERTIFICATE_HASH_VALUE +/* fatal */ +# define SSL_AD_UNKNOWN_PSK_IDENTITY TLS1_AD_UNKNOWN_PSK_IDENTITY +/* fatal */ +# define SSL_AD_INAPPROPRIATE_FALLBACK TLS1_AD_INAPPROPRIATE_FALLBACK +# define SSL_AD_NO_APPLICATION_PROTOCOL TLS1_AD_NO_APPLICATION_PROTOCOL +# define SSL_ERROR_NONE 0 +# define SSL_ERROR_SSL 1 +# define SSL_ERROR_WANT_READ 2 +# define SSL_ERROR_WANT_WRITE 3 +# define SSL_ERROR_WANT_X509_LOOKUP 4 +# define SSL_ERROR_SYSCALL 5/* look at error stack/return + * value/errno */ +# define SSL_ERROR_ZERO_RETURN 6 +# define SSL_ERROR_WANT_CONNECT 7 +# define SSL_ERROR_WANT_ACCEPT 8 +# define SSL_ERROR_WANT_ASYNC 9 +# define SSL_ERROR_WANT_ASYNC_JOB 10 +# define SSL_ERROR_WANT_CLIENT_HELLO_CB 11 +# define SSL_ERROR_WANT_RETRY_VERIFY 12 + +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_CTRL_SET_TMP_DH 3 +# define SSL_CTRL_SET_TMP_ECDH 4 +# define SSL_CTRL_SET_TMP_DH_CB 6 +# endif + +# define SSL_CTRL_GET_CLIENT_CERT_REQUEST 9 +# define SSL_CTRL_GET_NUM_RENEGOTIATIONS 10 +# define SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS 11 +# define SSL_CTRL_GET_TOTAL_RENEGOTIATIONS 12 +# define SSL_CTRL_GET_FLAGS 13 +# define SSL_CTRL_EXTRA_CHAIN_CERT 14 +# define SSL_CTRL_SET_MSG_CALLBACK 15 +# define SSL_CTRL_SET_MSG_CALLBACK_ARG 16 +/* only applies to datagram connections */ +# define SSL_CTRL_SET_MTU 17 +/* Stats */ +# define SSL_CTRL_SESS_NUMBER 20 +# define SSL_CTRL_SESS_CONNECT 21 +# define SSL_CTRL_SESS_CONNECT_GOOD 22 +# define SSL_CTRL_SESS_CONNECT_RENEGOTIATE 23 +# define SSL_CTRL_SESS_ACCEPT 24 +# define SSL_CTRL_SESS_ACCEPT_GOOD 25 +# define SSL_CTRL_SESS_ACCEPT_RENEGOTIATE 26 +# define SSL_CTRL_SESS_HIT 27 +# define SSL_CTRL_SESS_CB_HIT 28 +# define SSL_CTRL_SESS_MISSES 29 +# define SSL_CTRL_SESS_TIMEOUTS 30 +# define SSL_CTRL_SESS_CACHE_FULL 31 +# define SSL_CTRL_MODE 33 +# define SSL_CTRL_GET_READ_AHEAD 40 +# define SSL_CTRL_SET_READ_AHEAD 41 +# define SSL_CTRL_SET_SESS_CACHE_SIZE 42 +# define SSL_CTRL_GET_SESS_CACHE_SIZE 43 +# define SSL_CTRL_SET_SESS_CACHE_MODE 44 +# define SSL_CTRL_GET_SESS_CACHE_MODE 45 +# define SSL_CTRL_GET_MAX_CERT_LIST 50 +# define SSL_CTRL_SET_MAX_CERT_LIST 51 +# define SSL_CTRL_SET_MAX_SEND_FRAGMENT 52 +/* see tls1.h for macros based on these */ +# define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53 +# define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54 +# define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 +# define SSL_CTRL_SET_TLSEXT_DEBUG_CB 56 +# define SSL_CTRL_SET_TLSEXT_DEBUG_ARG 57 +# define SSL_CTRL_GET_TLSEXT_TICKET_KEYS 58 +# define SSL_CTRL_SET_TLSEXT_TICKET_KEYS 59 +/*# define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT 60 */ +/*# define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB 61 */ +/*# define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG 62 */ +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB 63 +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG 64 +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE 65 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS 66 +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS 67 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS 68 +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS 69 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP 70 +# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP 71 +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB 72 +# endif +# define SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB 75 +# define SSL_CTRL_SET_SRP_VERIFY_PARAM_CB 76 +# define SSL_CTRL_SET_SRP_GIVE_CLIENT_PWD_CB 77 +# define SSL_CTRL_SET_SRP_ARG 78 +# define SSL_CTRL_SET_TLS_EXT_SRP_USERNAME 79 +# define SSL_CTRL_SET_TLS_EXT_SRP_STRENGTH 80 +# define SSL_CTRL_SET_TLS_EXT_SRP_PASSWORD 81 +# define DTLS_CTRL_GET_TIMEOUT 73 +# define DTLS_CTRL_HANDLE_TIMEOUT 74 +# define SSL_CTRL_GET_RI_SUPPORT 76 +# define SSL_CTRL_CLEAR_MODE 78 +# define SSL_CTRL_SET_NOT_RESUMABLE_SESS_CB 79 +# define SSL_CTRL_GET_EXTRA_CHAIN_CERTS 82 +# define SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS 83 +# define SSL_CTRL_CHAIN 88 +# define SSL_CTRL_CHAIN_CERT 89 +# define SSL_CTRL_GET_GROUPS 90 +# define SSL_CTRL_SET_GROUPS 91 +# define SSL_CTRL_SET_GROUPS_LIST 92 +# define SSL_CTRL_GET_SHARED_GROUP 93 +# define SSL_CTRL_SET_SIGALGS 97 +# define SSL_CTRL_SET_SIGALGS_LIST 98 +# define SSL_CTRL_CERT_FLAGS 99 +# define SSL_CTRL_CLEAR_CERT_FLAGS 100 +# define SSL_CTRL_SET_CLIENT_SIGALGS 101 +# define SSL_CTRL_SET_CLIENT_SIGALGS_LIST 102 +# define SSL_CTRL_GET_CLIENT_CERT_TYPES 103 +# define SSL_CTRL_SET_CLIENT_CERT_TYPES 104 +# define SSL_CTRL_BUILD_CERT_CHAIN 105 +# define SSL_CTRL_SET_VERIFY_CERT_STORE 106 +# define SSL_CTRL_SET_CHAIN_CERT_STORE 107 +# define SSL_CTRL_GET_PEER_SIGNATURE_NID 108 +# define SSL_CTRL_GET_PEER_TMP_KEY 109 +# define SSL_CTRL_GET_RAW_CIPHERLIST 110 +# define SSL_CTRL_GET_EC_POINT_FORMATS 111 +# define SSL_CTRL_GET_CHAIN_CERTS 115 +# define SSL_CTRL_SELECT_CURRENT_CERT 116 +# define SSL_CTRL_SET_CURRENT_CERT 117 +# define SSL_CTRL_SET_DH_AUTO 118 +# define DTLS_CTRL_SET_LINK_MTU 120 +# define DTLS_CTRL_GET_LINK_MIN_MTU 121 +# define SSL_CTRL_GET_EXTMS_SUPPORT 122 +# define SSL_CTRL_SET_MIN_PROTO_VERSION 123 +# define SSL_CTRL_SET_MAX_PROTO_VERSION 124 +# define SSL_CTRL_SET_SPLIT_SEND_FRAGMENT 125 +# define SSL_CTRL_SET_MAX_PIPELINES 126 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_TYPE 127 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_CB 128 +# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_CB_ARG 129 +# define SSL_CTRL_GET_MIN_PROTO_VERSION 130 +# define SSL_CTRL_GET_MAX_PROTO_VERSION 131 +# define SSL_CTRL_GET_SIGNATURE_NID 132 +# define SSL_CTRL_GET_TMP_KEY 133 +# define SSL_CTRL_GET_NEGOTIATED_GROUP 134 +# define SSL_CTRL_GET_IANA_GROUPS 135 +# define SSL_CTRL_SET_RETRY_VERIFY 136 +# define SSL_CTRL_GET_VERIFY_CERT_STORE 137 +# define SSL_CTRL_GET_CHAIN_CERT_STORE 138 +# define SSL_CERT_SET_FIRST 1 +# define SSL_CERT_SET_NEXT 2 +# define SSL_CERT_SET_SERVER 3 +# define DTLSv1_get_timeout(ssl, arg) \ + SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)(arg)) +# define DTLSv1_handle_timeout(ssl) \ + SSL_ctrl(ssl,DTLS_CTRL_HANDLE_TIMEOUT,0, NULL) +# define SSL_num_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_GET_NUM_RENEGOTIATIONS,0,NULL) +# define SSL_clear_num_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS,0,NULL) +# define SSL_total_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_GET_TOTAL_RENEGOTIATIONS,0,NULL) +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_CTX_set_tmp_dh(ctx,dh) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_DH,0,(char *)(dh)) +# endif +# define SSL_CTX_set_dh_auto(ctx, onoff) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_DH_AUTO,onoff,NULL) +# define SSL_set_dh_auto(s, onoff) \ + SSL_ctrl(s,SSL_CTRL_SET_DH_AUTO,onoff,NULL) +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_set_tmp_dh(ssl,dh) \ + SSL_ctrl(ssl,SSL_CTRL_SET_TMP_DH,0,(char *)(dh)) +# endif +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_CTX_set_tmp_ecdh(ctx,ecdh) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_ECDH,0,(char *)(ecdh)) +# define SSL_set_tmp_ecdh(ssl,ecdh) \ + SSL_ctrl(ssl,SSL_CTRL_SET_TMP_ECDH,0,(char *)(ecdh)) +# endif +# define SSL_CTX_add_extra_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)(x509)) +# define SSL_CTX_get_extra_chain_certs(ctx,px509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_EXTRA_CHAIN_CERTS,0,px509) +# define SSL_CTX_get_extra_chain_certs_only(ctx,px509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_EXTRA_CHAIN_CERTS,1,px509) +# define SSL_CTX_clear_extra_chain_certs(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS,0,NULL) +# define SSL_CTX_set0_chain(ctx,sk) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,0,(char *)(sk)) +# define SSL_CTX_set1_chain(ctx,sk) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,1,(char *)(sk)) +# define SSL_CTX_add0_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)(x509)) +# define SSL_CTX_add1_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)(x509)) +# define SSL_CTX_get0_chain_certs(ctx,px509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERTS,0,px509) +# define SSL_CTX_clear_chain_certs(ctx) \ + SSL_CTX_set0_chain(ctx,NULL) +# define SSL_CTX_build_cert_chain(ctx, flags) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL) +# define SSL_CTX_select_current_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)(x509)) +# define SSL_CTX_set_current_cert(ctx, op) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CURRENT_CERT, op, NULL) +# define SSL_CTX_set0_verify_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)(st)) +# define SSL_CTX_set1_verify_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_VERIFY_CERT_STORE,1,(char *)(st)) +# define SSL_CTX_get0_verify_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_VERIFY_CERT_STORE,0,(char *)(st)) +# define SSL_CTX_set0_chain_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CHAIN_CERT_STORE,0,(char *)(st)) +# define SSL_CTX_set1_chain_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CHAIN_CERT_STORE,1,(char *)(st)) +# define SSL_CTX_get0_chain_cert_store(ctx,st) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERT_STORE,0,(char *)(st)) +# define SSL_set0_chain(s,sk) \ + SSL_ctrl(s,SSL_CTRL_CHAIN,0,(char *)(sk)) +# define SSL_set1_chain(s,sk) \ + SSL_ctrl(s,SSL_CTRL_CHAIN,1,(char *)(sk)) +# define SSL_add0_chain_cert(s,x509) \ + SSL_ctrl(s,SSL_CTRL_CHAIN_CERT,0,(char *)(x509)) +# define SSL_add1_chain_cert(s,x509) \ + SSL_ctrl(s,SSL_CTRL_CHAIN_CERT,1,(char *)(x509)) +# define SSL_get0_chain_certs(s,px509) \ + SSL_ctrl(s,SSL_CTRL_GET_CHAIN_CERTS,0,px509) +# define SSL_clear_chain_certs(s) \ + SSL_set0_chain(s,NULL) +# define SSL_build_cert_chain(s, flags) \ + SSL_ctrl(s,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL) +# define SSL_select_current_cert(s,x509) \ + SSL_ctrl(s,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)(x509)) +# define SSL_set_current_cert(s,op) \ + SSL_ctrl(s,SSL_CTRL_SET_CURRENT_CERT, op, NULL) +# define SSL_set0_verify_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)(st)) +# define SSL_set1_verify_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_SET_VERIFY_CERT_STORE,1,(char *)(st)) +#define SSL_get0_verify_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_GET_VERIFY_CERT_STORE,0,(char *)(st)) +# define SSL_set0_chain_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_SET_CHAIN_CERT_STORE,0,(char *)(st)) +# define SSL_set1_chain_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_SET_CHAIN_CERT_STORE,1,(char *)(st)) +#define SSL_get0_chain_cert_store(s,st) \ + SSL_ctrl(s,SSL_CTRL_GET_CHAIN_CERT_STORE,0,(char *)(st)) + +# define SSL_get1_groups(s, glist) \ + SSL_ctrl(s,SSL_CTRL_GET_GROUPS,0,(int*)(glist)) +# define SSL_get0_iana_groups(s, plst) \ + SSL_ctrl(s,SSL_CTRL_GET_IANA_GROUPS,0,(uint16_t **)(plst)) +# define SSL_CTX_set1_groups(ctx, glist, glistlen) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_GROUPS,glistlen,(int *)(glist)) +# define SSL_CTX_set1_groups_list(ctx, s) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_GROUPS_LIST,0,(char *)(s)) +# define SSL_set1_groups(s, glist, glistlen) \ + SSL_ctrl(s,SSL_CTRL_SET_GROUPS,glistlen,(char *)(glist)) +# define SSL_set1_groups_list(s, str) \ + SSL_ctrl(s,SSL_CTRL_SET_GROUPS_LIST,0,(char *)(str)) +# define SSL_get_shared_group(s, n) \ + SSL_ctrl(s,SSL_CTRL_GET_SHARED_GROUP,n,NULL) +# define SSL_get_negotiated_group(s) \ + SSL_ctrl(s,SSL_CTRL_GET_NEGOTIATED_GROUP,0,NULL) +# define SSL_CTX_set1_sigalgs(ctx, slist, slistlen) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SIGALGS,slistlen,(int *)(slist)) +# define SSL_CTX_set1_sigalgs_list(ctx, s) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SIGALGS_LIST,0,(char *)(s)) +# define SSL_set1_sigalgs(s, slist, slistlen) \ + SSL_ctrl(s,SSL_CTRL_SET_SIGALGS,slistlen,(int *)(slist)) +# define SSL_set1_sigalgs_list(s, str) \ + SSL_ctrl(s,SSL_CTRL_SET_SIGALGS_LIST,0,(char *)(str)) +# define SSL_CTX_set1_client_sigalgs(ctx, slist, slistlen) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CLIENT_SIGALGS,slistlen,(int *)(slist)) +# define SSL_CTX_set1_client_sigalgs_list(ctx, s) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CLIENT_SIGALGS_LIST,0,(char *)(s)) +# define SSL_set1_client_sigalgs(s, slist, slistlen) \ + SSL_ctrl(s,SSL_CTRL_SET_CLIENT_SIGALGS,slistlen,(int *)(slist)) +# define SSL_set1_client_sigalgs_list(s, str) \ + SSL_ctrl(s,SSL_CTRL_SET_CLIENT_SIGALGS_LIST,0,(char *)(str)) +# define SSL_get0_certificate_types(s, clist) \ + SSL_ctrl(s, SSL_CTRL_GET_CLIENT_CERT_TYPES, 0, (char *)(clist)) +# define SSL_CTX_set1_client_certificate_types(ctx, clist, clistlen) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CLIENT_CERT_TYPES,clistlen, \ + (char *)(clist)) +# define SSL_set1_client_certificate_types(s, clist, clistlen) \ + SSL_ctrl(s,SSL_CTRL_SET_CLIENT_CERT_TYPES,clistlen,(char *)(clist)) +# define SSL_get_signature_nid(s, pn) \ + SSL_ctrl(s,SSL_CTRL_GET_SIGNATURE_NID,0,pn) +# define SSL_get_peer_signature_nid(s, pn) \ + SSL_ctrl(s,SSL_CTRL_GET_PEER_SIGNATURE_NID,0,pn) +# define SSL_get_peer_tmp_key(s, pk) \ + SSL_ctrl(s,SSL_CTRL_GET_PEER_TMP_KEY,0,pk) +# define SSL_get_tmp_key(s, pk) \ + SSL_ctrl(s,SSL_CTRL_GET_TMP_KEY,0,pk) +# define SSL_get0_raw_cipherlist(s, plst) \ + SSL_ctrl(s,SSL_CTRL_GET_RAW_CIPHERLIST,0,plst) +# define SSL_get0_ec_point_formats(s, plst) \ + SSL_ctrl(s,SSL_CTRL_GET_EC_POINT_FORMATS,0,plst) +# define SSL_CTX_set_min_proto_version(ctx, version) \ + SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL) +# define SSL_CTX_set_max_proto_version(ctx, version) \ + SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL) +# define SSL_CTX_get_min_proto_version(ctx) \ + SSL_CTX_ctrl(ctx, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, NULL) +# define SSL_CTX_get_max_proto_version(ctx) \ + SSL_CTX_ctrl(ctx, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, NULL) +# define SSL_set_min_proto_version(s, version) \ + SSL_ctrl(s, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL) +# define SSL_set_max_proto_version(s, version) \ + SSL_ctrl(s, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL) +# define SSL_get_min_proto_version(s) \ + SSL_ctrl(s, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, NULL) +# define SSL_get_max_proto_version(s) \ + SSL_ctrl(s, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, NULL) + +const char *SSL_group_to_name(SSL *s, int id); + +/* Backwards compatibility, original 1.1.0 names */ +# define SSL_CTRL_GET_SERVER_TMP_KEY \ + SSL_CTRL_GET_PEER_TMP_KEY +# define SSL_get_server_tmp_key(s, pk) \ + SSL_get_peer_tmp_key(s, pk) + +int SSL_set0_tmp_dh_pkey(SSL *s, EVP_PKEY *dhpkey); +int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey); + +/* + * The following symbol names are old and obsolete. They are kept + * for compatibility reasons only and should not be used anymore. + */ +# define SSL_CTRL_GET_CURVES SSL_CTRL_GET_GROUPS +# define SSL_CTRL_SET_CURVES SSL_CTRL_SET_GROUPS +# define SSL_CTRL_SET_CURVES_LIST SSL_CTRL_SET_GROUPS_LIST +# define SSL_CTRL_GET_SHARED_CURVE SSL_CTRL_GET_SHARED_GROUP + +# define SSL_get1_curves SSL_get1_groups +# define SSL_CTX_set1_curves SSL_CTX_set1_groups +# define SSL_CTX_set1_curves_list SSL_CTX_set1_groups_list +# define SSL_set1_curves SSL_set1_groups +# define SSL_set1_curves_list SSL_set1_groups_list +# define SSL_get_shared_curve SSL_get_shared_group + + +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +/* Provide some compatibility macros for removed functionality. */ +# define SSL_CTX_need_tmp_RSA(ctx) 0 +# define SSL_CTX_set_tmp_rsa(ctx,rsa) 1 +# define SSL_need_tmp_RSA(ssl) 0 +# define SSL_set_tmp_rsa(ssl,rsa) 1 +# define SSL_CTX_set_ecdh_auto(dummy, onoff) ((onoff) != 0) +# define SSL_set_ecdh_auto(dummy, onoff) ((onoff) != 0) +/* + * We "pretend" to call the callback to avoid warnings about unused static + * functions. + */ +# define SSL_CTX_set_tmp_rsa_callback(ctx, cb) while(0) (cb)(NULL, 0, 0) +# define SSL_set_tmp_rsa_callback(ssl, cb) while(0) (cb)(NULL, 0, 0) +# endif +__owur const BIO_METHOD *BIO_f_ssl(void); +__owur BIO *BIO_new_ssl(SSL_CTX *ctx, int client); +__owur BIO *BIO_new_ssl_connect(SSL_CTX *ctx); +__owur BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx); +__owur int BIO_ssl_copy_session_id(BIO *to, BIO *from); +void BIO_ssl_shutdown(BIO *ssl_bio); + +__owur int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str); +__owur SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth); +__owur SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq, + const SSL_METHOD *meth); +int SSL_CTX_up_ref(SSL_CTX *ctx); +void SSL_CTX_free(SSL_CTX *); +__owur long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); +__owur long SSL_CTX_get_timeout(const SSL_CTX *ctx); +__owur X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); +void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); +void SSL_CTX_set1_cert_store(SSL_CTX *, X509_STORE *); +__owur int SSL_want(const SSL *s); +__owur int SSL_clear(SSL *s); + +void SSL_CTX_flush_sessions(SSL_CTX *ctx, long tm); + +__owur const SSL_CIPHER *SSL_get_current_cipher(const SSL *s); +__owur const SSL_CIPHER *SSL_get_pending_cipher(const SSL *s); +__owur int SSL_CIPHER_get_bits(const SSL_CIPHER *c, int *alg_bits); +__owur const char *SSL_CIPHER_get_version(const SSL_CIPHER *c); +__owur const char *SSL_CIPHER_get_name(const SSL_CIPHER *c); +__owur const char *SSL_CIPHER_standard_name(const SSL_CIPHER *c); +__owur const char *OPENSSL_cipher_name(const char *rfc_name); +__owur uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *c); +__owur uint16_t SSL_CIPHER_get_protocol_id(const SSL_CIPHER *c); +__owur int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c); +__owur int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c); +__owur const EVP_MD *SSL_CIPHER_get_handshake_digest(const SSL_CIPHER *c); +__owur int SSL_CIPHER_is_aead(const SSL_CIPHER *c); + +__owur int SSL_get_fd(const SSL *s); +__owur int SSL_get_rfd(const SSL *s); +__owur int SSL_get_wfd(const SSL *s); +__owur const char *SSL_get_cipher_list(const SSL *s, int n); +__owur char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size); +__owur int SSL_get_read_ahead(const SSL *s); +__owur int SSL_pending(const SSL *s); +__owur int SSL_has_pending(const SSL *s); +# ifndef OPENSSL_NO_SOCK +__owur int SSL_set_fd(SSL *s, int fd); +__owur int SSL_set_rfd(SSL *s, int fd); +__owur int SSL_set_wfd(SSL *s, int fd); +# endif +void SSL_set0_rbio(SSL *s, BIO *rbio); +void SSL_set0_wbio(SSL *s, BIO *wbio); +void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio); +__owur BIO *SSL_get_rbio(const SSL *s); +__owur BIO *SSL_get_wbio(const SSL *s); +__owur int SSL_set_cipher_list(SSL *s, const char *str); +__owur int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str); +__owur int SSL_set_ciphersuites(SSL *s, const char *str); +void SSL_set_read_ahead(SSL *s, int yes); +__owur int SSL_get_verify_mode(const SSL *s); +__owur int SSL_get_verify_depth(const SSL *s); +__owur SSL_verify_cb SSL_get_verify_callback(const SSL *s); +void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback); +void SSL_set_verify_depth(SSL *s, int depth); +void SSL_set_cert_cb(SSL *s, int (*cb) (SSL *ssl, void *arg), void *arg); +# ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 __owur int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa); +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, + const unsigned char *d, long len); +# endif +__owur int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey); +__owur int SSL_use_PrivateKey_ASN1(int pk, SSL *ssl, const unsigned char *d, + long len); +__owur int SSL_use_certificate(SSL *ssl, X509 *x); +__owur int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); +__owur int SSL_use_cert_and_key(SSL *ssl, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override); + + +/* serverinfo file format versions */ +# define SSL_SERVERINFOV1 1 +# define SSL_SERVERINFOV2 2 + +/* Set serverinfo data for the current active cert. */ +__owur int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, + size_t serverinfo_length); +__owur int SSL_CTX_use_serverinfo_ex(SSL_CTX *ctx, unsigned int version, + const unsigned char *serverinfo, + size_t serverinfo_length); +__owur int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); + +#ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type); +#endif + +__owur int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type); +__owur int SSL_use_certificate_file(SSL *ssl, const char *file, int type); + +#ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, + int type); +#endif +__owur int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, + int type); +__owur int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, + int type); +/* PEM type */ +__owur int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); +__owur int SSL_use_certificate_chain_file(SSL *ssl, const char *file); +__owur STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file); +__owur STACK_OF(X509_NAME) +*SSL_load_client_CA_file_ex(const char *file, OSSL_LIB_CTX *libctx, + const char *propq); +__owur int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs, + const char *file); +int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs, + const char *dir); +int SSL_add_store_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs, + const char *uri); + +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# define SSL_load_error_strings() \ + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \ + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL) +# endif + +__owur const char *SSL_state_string(const SSL *s); +__owur const char *SSL_rstate_string(const SSL *s); +__owur const char *SSL_state_string_long(const SSL *s); +__owur const char *SSL_rstate_string_long(const SSL *s); +__owur long SSL_SESSION_get_time(const SSL_SESSION *s); +__owur long SSL_SESSION_set_time(SSL_SESSION *s, long t); +__owur long SSL_SESSION_get_timeout(const SSL_SESSION *s); +__owur long SSL_SESSION_set_timeout(SSL_SESSION *s, long t); +__owur int SSL_SESSION_get_protocol_version(const SSL_SESSION *s); +__owur int SSL_SESSION_set_protocol_version(SSL_SESSION *s, int version); + +__owur const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s); +__owur int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname); +void SSL_SESSION_get0_alpn_selected(const SSL_SESSION *s, + const unsigned char **alpn, + size_t *len); +__owur int SSL_SESSION_set1_alpn_selected(SSL_SESSION *s, + const unsigned char *alpn, + size_t len); +__owur const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *s); +__owur int SSL_SESSION_set_cipher(SSL_SESSION *s, const SSL_CIPHER *cipher); +__owur int SSL_SESSION_has_ticket(const SSL_SESSION *s); +__owur unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s); +void SSL_SESSION_get0_ticket(const SSL_SESSION *s, const unsigned char **tick, + size_t *len); +__owur uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s); +__owur int SSL_SESSION_set_max_early_data(SSL_SESSION *s, + uint32_t max_early_data); +__owur int SSL_copy_session_id(SSL *to, const SSL *from); +__owur X509 *SSL_SESSION_get0_peer(SSL_SESSION *s); +__owur int SSL_SESSION_set1_id_context(SSL_SESSION *s, + const unsigned char *sid_ctx, + unsigned int sid_ctx_len); +__owur int SSL_SESSION_set1_id(SSL_SESSION *s, const unsigned char *sid, + unsigned int sid_len); +__owur int SSL_SESSION_is_resumable(const SSL_SESSION *s); + +__owur SSL_SESSION *SSL_SESSION_new(void); +__owur SSL_SESSION *SSL_SESSION_dup(const SSL_SESSION *src); +const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, + unsigned int *len); +const unsigned char *SSL_SESSION_get0_id_context(const SSL_SESSION *s, + unsigned int *len); +__owur unsigned int SSL_SESSION_get_compress_id(const SSL_SESSION *s); +# ifndef OPENSSL_NO_STDIO +int SSL_SESSION_print_fp(FILE *fp, const SSL_SESSION *ses); +# endif +int SSL_SESSION_print(BIO *fp, const SSL_SESSION *ses); +int SSL_SESSION_print_keylog(BIO *bp, const SSL_SESSION *x); +int SSL_SESSION_up_ref(SSL_SESSION *ses); +void SSL_SESSION_free(SSL_SESSION *ses); +__owur int i2d_SSL_SESSION(const SSL_SESSION *in, unsigned char **pp); +__owur int SSL_set_session(SSL *to, SSL_SESSION *session); +int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session); +int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *session); +__owur int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb); +__owur int SSL_set_generate_session_id(SSL *s, GEN_SESSION_CB cb); +__owur int SSL_has_matching_session_id(const SSL *s, + const unsigned char *id, + unsigned int id_len); +SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, + long length); + +# ifdef OPENSSL_X509_H +__owur X509 *SSL_get0_peer_certificate(const SSL *s); +__owur X509 *SSL_get1_peer_certificate(const SSL *s); +/* Deprecated in 3.0.0 */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# define SSL_get_peer_certificate SSL_get1_peer_certificate +# endif +# endif + +__owur STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s); + +__owur int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); +__owur int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); +__owur SSL_verify_cb SSL_CTX_get_verify_callback(const SSL_CTX *ctx); +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback); +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); +void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, + int (*cb) (X509_STORE_CTX *, void *), + void *arg); +void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb) (SSL *ssl, void *arg), + void *arg); +# ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, + long len); +# endif +__owur int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); +__owur int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, + const unsigned char *d, long len); +__owur int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); +__owur int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, + const unsigned char *d); +__owur int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override); + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u); +pem_password_cb *SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx); +void *SSL_CTX_get_default_passwd_cb_userdata(SSL_CTX *ctx); +void SSL_set_default_passwd_cb(SSL *s, pem_password_cb *cb); +void SSL_set_default_passwd_cb_userdata(SSL *s, void *u); +pem_password_cb *SSL_get_default_passwd_cb(SSL *s); +void *SSL_get_default_passwd_cb_userdata(SSL *s); + +__owur int SSL_CTX_check_private_key(const SSL_CTX *ctx); +__owur int SSL_check_private_key(const SSL *ctx); + +__owur int SSL_CTX_set_session_id_context(SSL_CTX *ctx, + const unsigned char *sid_ctx, + unsigned int sid_ctx_len); + +SSL *SSL_new(SSL_CTX *ctx); +int SSL_up_ref(SSL *s); +int SSL_is_dtls(const SSL *s); +__owur int SSL_set_session_id_context(SSL *ssl, const unsigned char *sid_ctx, + unsigned int sid_ctx_len); + +__owur int SSL_CTX_set_purpose(SSL_CTX *ctx, int purpose); +__owur int SSL_set_purpose(SSL *ssl, int purpose); +__owur int SSL_CTX_set_trust(SSL_CTX *ctx, int trust); +__owur int SSL_set_trust(SSL *ssl, int trust); + +__owur int SSL_set1_host(SSL *s, const char *hostname); +__owur int SSL_add1_host(SSL *s, const char *hostname); +__owur const char *SSL_get0_peername(SSL *s); +void SSL_set_hostflags(SSL *s, unsigned int flags); + +__owur int SSL_CTX_dane_enable(SSL_CTX *ctx); +__owur int SSL_CTX_dane_mtype_set(SSL_CTX *ctx, const EVP_MD *md, + uint8_t mtype, uint8_t ord); +__owur int SSL_dane_enable(SSL *s, const char *basedomain); +__owur int SSL_dane_tlsa_add(SSL *s, uint8_t usage, uint8_t selector, + uint8_t mtype, const unsigned char *data, size_t dlen); +__owur int SSL_get0_dane_authority(SSL *s, X509 **mcert, EVP_PKEY **mspki); +__owur int SSL_get0_dane_tlsa(SSL *s, uint8_t *usage, uint8_t *selector, + uint8_t *mtype, const unsigned char **data, + size_t *dlen); +/* + * Bridge opacity barrier between libcrypt and libssl, also needed to support + * offline testing in test/danetest.c + */ +SSL_DANE *SSL_get0_dane(SSL *ssl); +/* + * DANE flags + */ +unsigned long SSL_CTX_dane_set_flags(SSL_CTX *ctx, unsigned long flags); +unsigned long SSL_CTX_dane_clear_flags(SSL_CTX *ctx, unsigned long flags); +unsigned long SSL_dane_set_flags(SSL *ssl, unsigned long flags); +unsigned long SSL_dane_clear_flags(SSL *ssl, unsigned long flags); + +__owur int SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm); +__owur int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm); + +__owur X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx); +__owur X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl); + +# ifndef OPENSSL_NO_SRP +# ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 int SSL_CTX_set_srp_username(SSL_CTX *ctx, char *name); +OSSL_DEPRECATEDIN_3_0 int SSL_CTX_set_srp_password(SSL_CTX *ctx, char *password); +OSSL_DEPRECATEDIN_3_0 int SSL_CTX_set_srp_strength(SSL_CTX *ctx, int strength); +OSSL_DEPRECATEDIN_3_0 +int SSL_CTX_set_srp_client_pwd_callback(SSL_CTX *ctx, + char *(*cb) (SSL *, void *)); +OSSL_DEPRECATEDIN_3_0 +int SSL_CTX_set_srp_verify_param_callback(SSL_CTX *ctx, + int (*cb) (SSL *, void *)); +OSSL_DEPRECATEDIN_3_0 +int SSL_CTX_set_srp_username_callback(SSL_CTX *ctx, + int (*cb) (SSL *, int *, void *)); +OSSL_DEPRECATEDIN_3_0 int SSL_CTX_set_srp_cb_arg(SSL_CTX *ctx, void *arg); + +OSSL_DEPRECATEDIN_3_0 +int SSL_set_srp_server_param(SSL *s, const BIGNUM *N, const BIGNUM *g, + BIGNUM *sa, BIGNUM *v, char *info); +OSSL_DEPRECATEDIN_3_0 +int SSL_set_srp_server_param_pw(SSL *s, const char *user, const char *pass, + const char *grp); + +OSSL_DEPRECATEDIN_3_0 __owur BIGNUM *SSL_get_srp_g(SSL *s); +OSSL_DEPRECATEDIN_3_0 __owur BIGNUM *SSL_get_srp_N(SSL *s); + +OSSL_DEPRECATEDIN_3_0 __owur char *SSL_get_srp_username(SSL *s); +OSSL_DEPRECATEDIN_3_0 __owur char *SSL_get_srp_userinfo(SSL *s); +# endif +# endif + +/* + * ClientHello callback and helpers. + */ + +# define SSL_CLIENT_HELLO_SUCCESS 1 +# define SSL_CLIENT_HELLO_ERROR 0 +# define SSL_CLIENT_HELLO_RETRY (-1) + +typedef int (*SSL_client_hello_cb_fn) (SSL *s, int *al, void *arg); +void SSL_CTX_set_client_hello_cb(SSL_CTX *c, SSL_client_hello_cb_fn cb, + void *arg); +int SSL_client_hello_isv2(SSL *s); +unsigned int SSL_client_hello_get0_legacy_version(SSL *s); +size_t SSL_client_hello_get0_random(SSL *s, const unsigned char **out); +size_t SSL_client_hello_get0_session_id(SSL *s, const unsigned char **out); +size_t SSL_client_hello_get0_ciphers(SSL *s, const unsigned char **out); +size_t SSL_client_hello_get0_compression_methods(SSL *s, + const unsigned char **out); +int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen); +int SSL_client_hello_get_extension_order(SSL *s, uint16_t *exts, + size_t *num_exts); +int SSL_client_hello_get0_ext(SSL *s, unsigned int type, + const unsigned char **out, size_t *outlen); + +void SSL_certs_clear(SSL *s); +void SSL_free(SSL *ssl); +# ifdef OSSL_ASYNC_FD +/* + * Windows application developer has to include windows.h to use these. + */ +__owur int SSL_waiting_for_async(SSL *s); +__owur int SSL_get_all_async_fds(SSL *s, OSSL_ASYNC_FD *fds, size_t *numfds); +__owur int SSL_get_changed_async_fds(SSL *s, OSSL_ASYNC_FD *addfd, + size_t *numaddfds, OSSL_ASYNC_FD *delfd, + size_t *numdelfds); +__owur int SSL_CTX_set_async_callback(SSL_CTX *ctx, SSL_async_callback_fn callback); +__owur int SSL_CTX_set_async_callback_arg(SSL_CTX *ctx, void *arg); +__owur int SSL_set_async_callback(SSL *s, SSL_async_callback_fn callback); +__owur int SSL_set_async_callback_arg(SSL *s, void *arg); +__owur int SSL_get_async_status(SSL *s, int *status); + +# endif +__owur int SSL_accept(SSL *ssl); +__owur int SSL_stateless(SSL *s); +__owur int SSL_connect(SSL *ssl); +__owur int SSL_read(SSL *ssl, void *buf, int num); +__owur int SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes); + +# define SSL_READ_EARLY_DATA_ERROR 0 +# define SSL_READ_EARLY_DATA_SUCCESS 1 +# define SSL_READ_EARLY_DATA_FINISH 2 + +__owur int SSL_read_early_data(SSL *s, void *buf, size_t num, + size_t *readbytes); +__owur int SSL_peek(SSL *ssl, void *buf, int num); +__owur int SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes); +__owur ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, + int flags); +__owur int SSL_write(SSL *ssl, const void *buf, int num); +__owur int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written); +__owur int SSL_write_early_data(SSL *s, const void *buf, size_t num, + size_t *written); +long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg); +long SSL_callback_ctrl(SSL *, int, void (*)(void)); +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg); +long SSL_CTX_callback_ctrl(SSL_CTX *, int, void (*)(void)); + +# define SSL_EARLY_DATA_NOT_SENT 0 +# define SSL_EARLY_DATA_REJECTED 1 +# define SSL_EARLY_DATA_ACCEPTED 2 + +__owur int SSL_get_early_data_status(const SSL *s); + +__owur int SSL_get_error(const SSL *s, int ret_code); +__owur const char *SSL_get_version(const SSL *s); + +/* This sets the 'default' SSL version that SSL_new() will create */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +OSSL_DEPRECATEDIN_3_0 +__owur int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); +# endif + +# ifndef OPENSSL_NO_SSL3_METHOD +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *SSLv3_method(void); /* SSLv3 */ +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *SSLv3_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *SSLv3_client_method(void); +# endif +# endif + +#define SSLv23_method TLS_method +#define SSLv23_server_method TLS_server_method +#define SSLv23_client_method TLS_client_method + +/* Negotiate highest available SSL/TLS version */ +__owur const SSL_METHOD *TLS_method(void); +__owur const SSL_METHOD *TLS_server_method(void); +__owur const SSL_METHOD *TLS_client_method(void); + +# ifndef OPENSSL_NO_TLS1_METHOD +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_method(void); /* TLSv1.0 */ +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_client_method(void); +# endif +# endif + +# ifndef OPENSSL_NO_TLS1_1_METHOD +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_1_method(void); /* TLSv1.1 */ +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_1_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_1_client_method(void); +# endif +# endif + +# ifndef OPENSSL_NO_TLS1_2_METHOD +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_2_method(void); /* TLSv1.2 */ +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_2_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *TLSv1_2_client_method(void); +# endif +# endif + +# ifndef OPENSSL_NO_DTLS1_METHOD +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_method(void); /* DTLSv1.0 */ +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_client_method(void); +# endif +# endif + +# ifndef OPENSSL_NO_DTLS1_2_METHOD +/* DTLSv1.2 */ +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_2_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_2_server_method(void); +OSSL_DEPRECATEDIN_1_1_0 __owur const SSL_METHOD *DTLSv1_2_client_method(void); +# endif +# endif + +__owur const SSL_METHOD *DTLS_method(void); /* DTLS 1.0 and 1.2 */ +__owur const SSL_METHOD *DTLS_server_method(void); /* DTLS 1.0 and 1.2 */ +__owur const SSL_METHOD *DTLS_client_method(void); /* DTLS 1.0 and 1.2 */ + +__owur size_t DTLS_get_data_mtu(const SSL *s); + +__owur STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s); +__owur STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx); +__owur STACK_OF(SSL_CIPHER) *SSL_get_client_ciphers(const SSL *s); +__owur STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s); + +__owur int SSL_do_handshake(SSL *s); +int SSL_key_update(SSL *s, int updatetype); +int SSL_get_key_update_type(const SSL *s); +int SSL_renegotiate(SSL *s); +int SSL_renegotiate_abbreviated(SSL *s); +__owur int SSL_renegotiate_pending(const SSL *s); +int SSL_new_session_ticket(SSL *s); +int SSL_shutdown(SSL *s); +__owur int SSL_verify_client_post_handshake(SSL *s); +void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val); +void SSL_set_post_handshake_auth(SSL *s, int val); + +__owur const SSL_METHOD *SSL_CTX_get_ssl_method(const SSL_CTX *ctx); +__owur const SSL_METHOD *SSL_get_ssl_method(const SSL *s); +__owur int SSL_set_ssl_method(SSL *s, const SSL_METHOD *method); +__owur const char *SSL_alert_type_string_long(int value); +__owur const char *SSL_alert_type_string(int value); +__owur const char *SSL_alert_desc_string_long(int value); +__owur const char *SSL_alert_desc_string(int value); + +void SSL_set0_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list); +void SSL_CTX_set0_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); +__owur const STACK_OF(X509_NAME) *SSL_get0_CA_list(const SSL *s); +__owur const STACK_OF(X509_NAME) *SSL_CTX_get0_CA_list(const SSL_CTX *ctx); +__owur int SSL_add1_to_CA_list(SSL *ssl, const X509 *x); +__owur int SSL_CTX_add1_to_CA_list(SSL_CTX *ctx, const X509 *x); +__owur const STACK_OF(X509_NAME) *SSL_get0_peer_CA_list(const SSL *s); + +void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list); +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); +__owur STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s); +__owur STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *s); +__owur int SSL_add_client_CA(SSL *ssl, X509 *x); +__owur int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +void SSL_set_connect_state(SSL *s); +void SSL_set_accept_state(SSL *s); + +__owur long SSL_get_default_timeout(const SSL *s); + +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# define SSL_library_init() OPENSSL_init_ssl(0, NULL) +# endif + +__owur char *SSL_CIPHER_description(const SSL_CIPHER *, char *buf, int size); +__owur STACK_OF(X509_NAME) *SSL_dup_CA_list(const STACK_OF(X509_NAME) *sk); + +__owur SSL *SSL_dup(SSL *ssl); + +__owur X509 *SSL_get_certificate(const SSL *ssl); +/* + * EVP_PKEY + */ +struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl); + +__owur X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); +__owur EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx); + +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); +__owur int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); +void SSL_set_quiet_shutdown(SSL *ssl, int mode); +__owur int SSL_get_quiet_shutdown(const SSL *ssl); +void SSL_set_shutdown(SSL *ssl, int mode); +__owur int SSL_get_shutdown(const SSL *ssl); +__owur int SSL_version(const SSL *ssl); +__owur int SSL_client_version(const SSL *s); +__owur int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); +__owur int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx); +__owur int SSL_CTX_set_default_verify_file(SSL_CTX *ctx); +__owur int SSL_CTX_set_default_verify_store(SSL_CTX *ctx); +__owur int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile); +__owur int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath); +__owur int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore); +__owur int SSL_CTX_load_verify_locations(SSL_CTX *ctx, + const char *CAfile, + const char *CApath); +# define SSL_get0_session SSL_get_session/* just peek at pointer */ +__owur SSL_SESSION *SSL_get_session(const SSL *ssl); +__owur SSL_SESSION *SSL_get1_session(SSL *ssl); /* obtain a reference count */ +__owur SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); +SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx); +void SSL_set_info_callback(SSL *ssl, + void (*cb) (const SSL *ssl, int type, int val)); +void (*SSL_get_info_callback(const SSL *ssl)) (const SSL *ssl, int type, + int val); +__owur OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + +void SSL_set_verify_result(SSL *ssl, long v); +__owur long SSL_get_verify_result(const SSL *ssl); +__owur STACK_OF(X509) *SSL_get0_verified_chain(const SSL *s); + +__owur size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, + size_t outlen); +__owur size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, + size_t outlen); +__owur size_t SSL_SESSION_get_master_key(const SSL_SESSION *sess, + unsigned char *out, size_t outlen); +__owur int SSL_SESSION_set1_master_key(SSL_SESSION *sess, + const unsigned char *in, size_t len); +uint8_t SSL_SESSION_get_max_fragment_length(const SSL_SESSION *sess); + +#define SSL_get_ex_new_index(l, p, newf, dupf, freef) \ + CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, l, p, newf, dupf, freef) +__owur int SSL_set_ex_data(SSL *ssl, int idx, void *data); +void *SSL_get_ex_data(const SSL *ssl, int idx); +#define SSL_SESSION_get_ex_new_index(l, p, newf, dupf, freef) \ + CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_SESSION, l, p, newf, dupf, freef) +__owur int SSL_SESSION_set_ex_data(SSL_SESSION *ss, int idx, void *data); +void *SSL_SESSION_get_ex_data(const SSL_SESSION *ss, int idx); +#define SSL_CTX_get_ex_new_index(l, p, newf, dupf, freef) \ + CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, l, p, newf, dupf, freef) +__owur int SSL_CTX_set_ex_data(SSL_CTX *ssl, int idx, void *data); +void *SSL_CTX_get_ex_data(const SSL_CTX *ssl, int idx); + +__owur int SSL_get_ex_data_X509_STORE_CTX_idx(void); + +# define SSL_CTX_sess_set_cache_size(ctx,t) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_SIZE,t,NULL) +# define SSL_CTX_sess_get_cache_size(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_SIZE,0,NULL) +# define SSL_CTX_set_session_cache_mode(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL) +# define SSL_CTX_get_session_cache_mode(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_MODE,0,NULL) + +# define SSL_CTX_get_default_read_ahead(ctx) SSL_CTX_get_read_ahead(ctx) +# define SSL_CTX_set_default_read_ahead(ctx,m) SSL_CTX_set_read_ahead(ctx,m) +# define SSL_CTX_get_read_ahead(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_READ_AHEAD,0,NULL) +# define SSL_CTX_set_read_ahead(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_READ_AHEAD,m,NULL) +# define SSL_CTX_get_max_cert_list(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_MAX_CERT_LIST,0,NULL) +# define SSL_CTX_set_max_cert_list(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_MAX_CERT_LIST,m,NULL) +# define SSL_get_max_cert_list(ssl) \ + SSL_ctrl(ssl,SSL_CTRL_GET_MAX_CERT_LIST,0,NULL) +# define SSL_set_max_cert_list(ssl,m) \ + SSL_ctrl(ssl,SSL_CTRL_SET_MAX_CERT_LIST,m,NULL) + +# define SSL_CTX_set_max_send_fragment(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_MAX_SEND_FRAGMENT,m,NULL) +# define SSL_set_max_send_fragment(ssl,m) \ + SSL_ctrl(ssl,SSL_CTRL_SET_MAX_SEND_FRAGMENT,m,NULL) +# define SSL_CTX_set_split_send_fragment(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SPLIT_SEND_FRAGMENT,m,NULL) +# define SSL_set_split_send_fragment(ssl,m) \ + SSL_ctrl(ssl,SSL_CTRL_SET_SPLIT_SEND_FRAGMENT,m,NULL) +# define SSL_CTX_set_max_pipelines(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_MAX_PIPELINES,m,NULL) +# define SSL_set_max_pipelines(ssl,m) \ + SSL_ctrl(ssl,SSL_CTRL_SET_MAX_PIPELINES,m,NULL) +# define SSL_set_retry_verify(ssl) \ + (SSL_ctrl(ssl,SSL_CTRL_SET_RETRY_VERIFY,0,NULL) > 0) + +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); +void SSL_set_default_read_buffer_len(SSL *s, size_t len); + +# ifndef OPENSSL_NO_DH +# ifndef OPENSSL_NO_DEPRECATED_3_0 +/* NB: the |keylength| is only applicable when is_export is true */ +OSSL_DEPRECATEDIN_3_0 +void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, + DH *(*dh) (SSL *ssl, int is_export, + int keylength)); +OSSL_DEPRECATEDIN_3_0 +void SSL_set_tmp_dh_callback(SSL *ssl, + DH *(*dh) (SSL *ssl, int is_export, + int keylength)); +# endif +# endif + +__owur const COMP_METHOD *SSL_get_current_compression(const SSL *s); +__owur const COMP_METHOD *SSL_get_current_expansion(const SSL *s); +__owur const char *SSL_COMP_get_name(const COMP_METHOD *comp); +__owur const char *SSL_COMP_get0_name(const SSL_COMP *comp); +__owur int SSL_COMP_get_id(const SSL_COMP *comp); +STACK_OF(SSL_COMP) *SSL_COMP_get_compression_methods(void); +__owur STACK_OF(SSL_COMP) *SSL_COMP_set0_compression_methods(STACK_OF(SSL_COMP) + *meths); +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# define SSL_COMP_free_compression_methods() while(0) continue +# endif +__owur int SSL_COMP_add_compression_method(int id, COMP_METHOD *cm); + +const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr); +int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *c); +int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *c); +int SSL_bytes_to_cipher_list(SSL *s, const unsigned char *bytes, size_t len, + int isv2format, STACK_OF(SSL_CIPHER) **sk, + STACK_OF(SSL_CIPHER) **scsvs); + +/* TLS extensions functions */ +__owur int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); + +__owur int SSL_set_session_ticket_ext_cb(SSL *s, + tls_session_ticket_ext_cb_fn cb, + void *arg); + +/* Pre-shared secret session resumption functions */ +__owur int SSL_set_session_secret_cb(SSL *s, + tls_session_secret_cb_fn session_secret_cb, + void *arg); + +void SSL_CTX_set_not_resumable_session_callback(SSL_CTX *ctx, + int (*cb) (SSL *ssl, + int + is_forward_secure)); + +void SSL_set_not_resumable_session_callback(SSL *ssl, + int (*cb) (SSL *ssl, + int is_forward_secure)); + +void SSL_CTX_set_record_padding_callback(SSL_CTX *ctx, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)); +void SSL_CTX_set_record_padding_callback_arg(SSL_CTX *ctx, void *arg); +void *SSL_CTX_get_record_padding_callback_arg(const SSL_CTX *ctx); +int SSL_CTX_set_block_padding(SSL_CTX *ctx, size_t block_size); + +int SSL_set_record_padding_callback(SSL *ssl, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)); +void SSL_set_record_padding_callback_arg(SSL *ssl, void *arg); +void *SSL_get_record_padding_callback_arg(const SSL *ssl); +int SSL_set_block_padding(SSL *ssl, size_t block_size); + +int SSL_set_num_tickets(SSL *s, size_t num_tickets); +size_t SSL_get_num_tickets(const SSL *s); +int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets); +size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx); + +# ifndef OPENSSL_NO_DEPRECATED_1_1_0 +# define SSL_cache_hit(s) SSL_session_reused(s) +# endif + +__owur int SSL_session_reused(const SSL *s); +__owur int SSL_is_server(const SSL *s); + +__owur __owur SSL_CONF_CTX *SSL_CONF_CTX_new(void); +int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx); +void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx); +unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags); +__owur unsigned int SSL_CONF_CTX_clear_flags(SSL_CONF_CTX *cctx, + unsigned int flags); +__owur int SSL_CONF_CTX_set1_prefix(SSL_CONF_CTX *cctx, const char *pre); + +void SSL_CONF_CTX_set_ssl(SSL_CONF_CTX *cctx, SSL *ssl); +void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx); + +__owur int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value); +__owur int SSL_CONF_cmd_argv(SSL_CONF_CTX *cctx, int *pargc, char ***pargv); +__owur int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd); + +void SSL_add_ssl_module(void); +int SSL_config(SSL *s, const char *name); +int SSL_CTX_config(SSL_CTX *ctx, const char *name); + +# ifndef OPENSSL_NO_SSL_TRACE +void SSL_trace(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg); +# endif + +# ifndef OPENSSL_NO_SOCK +int DTLSv1_listen(SSL *s, BIO_ADDR *client); +# endif + +# ifndef OPENSSL_NO_CT + +/* + * A callback for verifying that the received SCTs are sufficient. + * Expected to return 1 if they are sufficient, otherwise 0. + * May return a negative integer if an error occurs. + * A connection should be aborted if the SCTs are deemed insufficient. + */ +typedef int (*ssl_ct_validation_cb)(const CT_POLICY_EVAL_CTX *ctx, + const STACK_OF(SCT) *scts, void *arg); + +/* + * Sets a |callback| that is invoked upon receipt of ServerHelloDone to validate + * the received SCTs. + * If the callback returns a non-positive result, the connection is terminated. + * Call this function before beginning a handshake. + * If a NULL |callback| is provided, SCT validation is disabled. + * |arg| is arbitrary userdata that will be passed to the callback whenever it + * is invoked. Ownership of |arg| remains with the caller. + * + * NOTE: A side-effect of setting a CT callback is that an OCSP stapled response + * will be requested. + */ +int SSL_set_ct_validation_callback(SSL *s, ssl_ct_validation_cb callback, + void *arg); +int SSL_CTX_set_ct_validation_callback(SSL_CTX *ctx, + ssl_ct_validation_cb callback, + void *arg); +#define SSL_disable_ct(s) \ + ((void) SSL_set_validation_callback((s), NULL, NULL)) +#define SSL_CTX_disable_ct(ctx) \ + ((void) SSL_CTX_set_validation_callback((ctx), NULL, NULL)) + +/* + * The validation type enumerates the available behaviours of the built-in SSL + * CT validation callback selected via SSL_enable_ct() and SSL_CTX_enable_ct(). + * The underlying callback is a static function in libssl. + */ +enum { + SSL_CT_VALIDATION_PERMISSIVE = 0, + SSL_CT_VALIDATION_STRICT +}; + +/* + * Enable CT by setting up a callback that implements one of the built-in + * validation variants. The SSL_CT_VALIDATION_PERMISSIVE variant always + * continues the handshake, the application can make appropriate decisions at + * handshake completion. The SSL_CT_VALIDATION_STRICT variant requires at + * least one valid SCT, or else handshake termination will be requested. The + * handshake may continue anyway if SSL_VERIFY_NONE is in effect. + */ +int SSL_enable_ct(SSL *s, int validation_mode); +int SSL_CTX_enable_ct(SSL_CTX *ctx, int validation_mode); + +/* + * Report whether a non-NULL callback is enabled. + */ +int SSL_ct_is_enabled(const SSL *s); +int SSL_CTX_ct_is_enabled(const SSL_CTX *ctx); + +/* Gets the SCTs received from a connection */ +const STACK_OF(SCT) *SSL_get0_peer_scts(SSL *s); + +/* + * Loads the CT log list from the default location. + * If a CTLOG_STORE has previously been set using SSL_CTX_set_ctlog_store, + * the log information loaded from this file will be appended to the + * CTLOG_STORE. + * Returns 1 on success, 0 otherwise. + */ +int SSL_CTX_set_default_ctlog_list_file(SSL_CTX *ctx); + +/* + * Loads the CT log list from the specified file path. + * If a CTLOG_STORE has previously been set using SSL_CTX_set_ctlog_store, + * the log information loaded from this file will be appended to the + * CTLOG_STORE. + * Returns 1 on success, 0 otherwise. + */ +int SSL_CTX_set_ctlog_list_file(SSL_CTX *ctx, const char *path); + +/* + * Sets the CT log list used by all SSL connections created from this SSL_CTX. + * Ownership of the CTLOG_STORE is transferred to the SSL_CTX. + */ +void SSL_CTX_set0_ctlog_store(SSL_CTX *ctx, CTLOG_STORE *logs); + +/* + * Gets the CT log list used by all SSL connections created from this SSL_CTX. + * This will be NULL unless one of the following functions has been called: + * - SSL_CTX_set_default_ctlog_list_file + * - SSL_CTX_set_ctlog_list_file + * - SSL_CTX_set_ctlog_store + */ +const CTLOG_STORE *SSL_CTX_get0_ctlog_store(const SSL_CTX *ctx); + +# endif /* OPENSSL_NO_CT */ + +/* What the "other" parameter contains in security callback */ +/* Mask for type */ +# define SSL_SECOP_OTHER_TYPE 0xffff0000 +# define SSL_SECOP_OTHER_NONE 0 +# define SSL_SECOP_OTHER_CIPHER (1 << 16) +# define SSL_SECOP_OTHER_CURVE (2 << 16) +# define SSL_SECOP_OTHER_DH (3 << 16) +# define SSL_SECOP_OTHER_PKEY (4 << 16) +# define SSL_SECOP_OTHER_SIGALG (5 << 16) +# define SSL_SECOP_OTHER_CERT (6 << 16) + +/* Indicated operation refers to peer key or certificate */ +# define SSL_SECOP_PEER 0x1000 + +/* Values for "op" parameter in security callback */ + +/* Called to filter ciphers */ +/* Ciphers client supports */ +# define SSL_SECOP_CIPHER_SUPPORTED (1 | SSL_SECOP_OTHER_CIPHER) +/* Cipher shared by client/server */ +# define SSL_SECOP_CIPHER_SHARED (2 | SSL_SECOP_OTHER_CIPHER) +/* Sanity check of cipher server selects */ +# define SSL_SECOP_CIPHER_CHECK (3 | SSL_SECOP_OTHER_CIPHER) +/* Curves supported by client */ +# define SSL_SECOP_CURVE_SUPPORTED (4 | SSL_SECOP_OTHER_CURVE) +/* Curves shared by client/server */ +# define SSL_SECOP_CURVE_SHARED (5 | SSL_SECOP_OTHER_CURVE) +/* Sanity check of curve server selects */ +# define SSL_SECOP_CURVE_CHECK (6 | SSL_SECOP_OTHER_CURVE) +/* Temporary DH key */ +# define SSL_SECOP_TMP_DH (7 | SSL_SECOP_OTHER_PKEY) +/* SSL/TLS version */ +# define SSL_SECOP_VERSION (9 | SSL_SECOP_OTHER_NONE) +/* Session tickets */ +# define SSL_SECOP_TICKET (10 | SSL_SECOP_OTHER_NONE) +/* Supported signature algorithms sent to peer */ +# define SSL_SECOP_SIGALG_SUPPORTED (11 | SSL_SECOP_OTHER_SIGALG) +/* Shared signature algorithm */ +# define SSL_SECOP_SIGALG_SHARED (12 | SSL_SECOP_OTHER_SIGALG) +/* Sanity check signature algorithm allowed */ +# define SSL_SECOP_SIGALG_CHECK (13 | SSL_SECOP_OTHER_SIGALG) +/* Used to get mask of supported public key signature algorithms */ +# define SSL_SECOP_SIGALG_MASK (14 | SSL_SECOP_OTHER_SIGALG) +/* Use to see if compression is allowed */ +# define SSL_SECOP_COMPRESSION (15 | SSL_SECOP_OTHER_NONE) +/* EE key in certificate */ +# define SSL_SECOP_EE_KEY (16 | SSL_SECOP_OTHER_CERT) +/* CA key in certificate */ +# define SSL_SECOP_CA_KEY (17 | SSL_SECOP_OTHER_CERT) +/* CA digest algorithm in certificate */ +# define SSL_SECOP_CA_MD (18 | SSL_SECOP_OTHER_CERT) +/* Peer EE key in certificate */ +# define SSL_SECOP_PEER_EE_KEY (SSL_SECOP_EE_KEY | SSL_SECOP_PEER) +/* Peer CA key in certificate */ +# define SSL_SECOP_PEER_CA_KEY (SSL_SECOP_CA_KEY | SSL_SECOP_PEER) +/* Peer CA digest algorithm in certificate */ +# define SSL_SECOP_PEER_CA_MD (SSL_SECOP_CA_MD | SSL_SECOP_PEER) + +void SSL_set_security_level(SSL *s, int level); +__owur int SSL_get_security_level(const SSL *s); +void SSL_set_security_callback(SSL *s, + int (*cb) (const SSL *s, const SSL_CTX *ctx, + int op, int bits, int nid, + void *other, void *ex)); +int (*SSL_get_security_callback(const SSL *s)) (const SSL *s, + const SSL_CTX *ctx, int op, + int bits, int nid, void *other, + void *ex); +void SSL_set0_security_ex_data(SSL *s, void *ex); +__owur void *SSL_get0_security_ex_data(const SSL *s); + +void SSL_CTX_set_security_level(SSL_CTX *ctx, int level); +__owur int SSL_CTX_get_security_level(const SSL_CTX *ctx); +void SSL_CTX_set_security_callback(SSL_CTX *ctx, + int (*cb) (const SSL *s, const SSL_CTX *ctx, + int op, int bits, int nid, + void *other, void *ex)); +int (*SSL_CTX_get_security_callback(const SSL_CTX *ctx)) (const SSL *s, + const SSL_CTX *ctx, + int op, int bits, + int nid, + void *other, + void *ex); +void SSL_CTX_set0_security_ex_data(SSL_CTX *ctx, void *ex); +__owur void *SSL_CTX_get0_security_ex_data(const SSL_CTX *ctx); + +/* OPENSSL_INIT flag 0x010000 reserved for internal use */ +# define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L +# define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L + +# define OPENSSL_INIT_SSL_DEFAULT \ + (OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS) + +int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); + +# ifndef OPENSSL_NO_UNIT_TEST +__owur const struct openssl_ssl_test_functions *SSL_test_functions(void); +# endif + +__owur int SSL_free_buffers(SSL *ssl); +__owur int SSL_alloc_buffers(SSL *ssl); + +/* Status codes passed to the decrypt session ticket callback. Some of these + * are for internal use only and are never passed to the callback. */ +typedef int SSL_TICKET_STATUS; + +/* Support for ticket appdata */ +/* fatal error, malloc failure */ +# define SSL_TICKET_FATAL_ERR_MALLOC 0 +/* fatal error, either from parsing or decrypting the ticket */ +# define SSL_TICKET_FATAL_ERR_OTHER 1 +/* No ticket present */ +# define SSL_TICKET_NONE 2 +/* Empty ticket present */ +# define SSL_TICKET_EMPTY 3 +/* the ticket couldn't be decrypted */ +# define SSL_TICKET_NO_DECRYPT 4 +/* a ticket was successfully decrypted */ +# define SSL_TICKET_SUCCESS 5 +/* same as above but the ticket needs to be renewed */ +# define SSL_TICKET_SUCCESS_RENEW 6 + +/* Return codes for the decrypt session ticket callback */ +typedef int SSL_TICKET_RETURN; + +/* An error occurred */ +#define SSL_TICKET_RETURN_ABORT 0 +/* Do not use the ticket, do not send a renewed ticket to the client */ +#define SSL_TICKET_RETURN_IGNORE 1 +/* Do not use the ticket, send a renewed ticket to the client */ +#define SSL_TICKET_RETURN_IGNORE_RENEW 2 +/* Use the ticket, do not send a renewed ticket to the client */ +#define SSL_TICKET_RETURN_USE 3 +/* Use the ticket, send a renewed ticket to the client */ +#define SSL_TICKET_RETURN_USE_RENEW 4 + +typedef int (*SSL_CTX_generate_session_ticket_fn)(SSL *s, void *arg); +typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss, + const unsigned char *keyname, + size_t keyname_length, + SSL_TICKET_STATUS status, + void *arg); +int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx, + SSL_CTX_generate_session_ticket_fn gen_cb, + SSL_CTX_decrypt_session_ticket_fn dec_cb, + void *arg); +int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len); +int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len); + +typedef unsigned int (*DTLS_timer_cb)(SSL *s, unsigned int timer_us); + +void DTLS_set_timer_cb(SSL *s, DTLS_timer_cb cb); + + +typedef int (*SSL_allow_early_data_cb_fn)(SSL *s, void *arg); +void SSL_CTX_set_allow_early_data_cb(SSL_CTX *ctx, + SSL_allow_early_data_cb_fn cb, + void *arg); +void SSL_set_allow_early_data_cb(SSL *s, + SSL_allow_early_data_cb_fn cb, + void *arg); + +/* store the default cipher strings inside the library */ +const char *OSSL_default_cipher_list(void); +const char *OSSL_default_ciphersuites(void); + +# ifdef __cplusplus +} +# endif +#endif diff --git a/tools/deps/custom_openssl_ech_draft/patches/0001-HPKE-build-fix.patch b/tools/deps/custom_openssl_ech_draft/patches/0001-HPKE-build-fix.patch new file mode 100644 index 000000000..f9522ce29 --- /dev/null +++ b/tools/deps/custom_openssl_ech_draft/patches/0001-HPKE-build-fix.patch @@ -0,0 +1,24 @@ +From c22d8496fe7c2e15f4288b64a945f2bb0d631ad1 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Jun 2023 02:56:19 +0300 +Subject: [PATCH 1/2] HPKE build fix + +--- + crypto/hpke/hpke.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c +index 653b3a2..d936e1c 100644 +--- a/crypto/hpke/hpke.c ++++ b/crypto/hpke/hpke.c +@@ -16,7 +16,7 @@ + #include + #include + +-#include ++//#include + + #include + #include +-- +2.40.1.windows.1 + diff --git a/tools/deps/custom_openssl_ech_draft/patches/0002-Super-large-padding-extension-SSL_OP_TLSEXT_PADDING_.patch b/tools/deps/custom_openssl_ech_draft/patches/0002-Super-large-padding-extension-SSL_OP_TLSEXT_PADDING_.patch new file mode 100644 index 000000000..ff634739e --- /dev/null +++ b/tools/deps/custom_openssl_ech_draft/patches/0002-Super-large-padding-extension-SSL_OP_TLSEXT_PADDING_.patch @@ -0,0 +1,42 @@ +From cc876a7d66cbf96fbb9f5d64fc631951f695b953 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Jun 2023 02:57:43 +0300 +Subject: [PATCH 2/2] Super large padding extension SSL_OP_TLSEXT_PADDING_SUPER + option + +--- + include/openssl/ssl.h.in | 2 ++ + ssl/statem/extensions_clnt.c | 5 +++++ + 2 files changed, 7 insertions(+) + +diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in +index fce4dec..9ae1f4a 100644 +--- a/include/openssl/ssl.h.in ++++ b/include/openssl/ssl.h.in +@@ -423,6 +423,8 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg); + * that with a random value */ + #define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(34) + #endif ++/* Super large padding to bypass non-reassembling censorship filters */ ++#define SSL_OP_TLSEXT_PADDING_SUPER SSL_OP_BIT(35) + + /* + * Option "collections." +diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c +index c9397d7..e39d510 100644 +--- a/ssl/statem/extensions_clnt.c ++++ b/ssl/statem/extensions_clnt.c +@@ -1221,6 +1221,11 @@ EXT_RETURN tls_construct_ctos_padding(SSL *s, WPACKET *pkt, + if (hlen > F5_WORKAROUND_MIN_MSG_LEN && hlen < F5_WORKAROUND_MAX_MSG_LEN) { + /* Calculate the amount of padding we need to add */ + hlen = F5_WORKAROUND_MAX_MSG_LEN - hlen; ++ /* Super large padding for circumvention of non-reassembling boxes */ ++ if (s->options & SSL_OP_TLSEXT_PADDING_SUPER) { ++ RAND_bytes(&hlen, sizeof(hlen)); ++ hlen = 2000 + (hlen % 10000); ++ } + + /* + * Take off the size of extension header itself (2 bytes for type and +-- +2.40.1.windows.1 + diff --git a/tools/deps/custom_openssl_ech_draft/ssl/statem/extensions_clnt.c b/tools/deps/custom_openssl_ech_draft/ssl/statem/extensions_clnt.c new file mode 100644 index 000000000..e39d51036 --- /dev/null +++ b/tools/deps/custom_openssl_ech_draft/ssl/statem/extensions_clnt.c @@ -0,0 +1,2522 @@ +/* + * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "../ssl_local.h" +#include "internal/cryptlib.h" +#include "statem_local.h" + +#ifndef OPENSSL_NO_ECH +#include + +/* + * Handle inner/outer CH cloning - ech_same_ext will + * (depending on ech.c compile time options) copy the + * value from CH.inner to CH.outer or else processing + * will continue, generating a new value for the outer + * CH. + * This macro should be called in each _ctos_ function + * that doesn't explicitly need special handling. See + * the many examples below. + * + * Note that the placement of this macro needs a bit + * of thought - it has to go after declarations (to + * keep the ansi-c compile happy) but also after any + * checks that result in the extension not being sent. + */ +#define IOSAME if (s->ech && !s->ext.ech_grease) { \ + int __rv=ech_same_ext(s,pkt); \ + if (__rv==ECH_SAME_EXT_ERR) return(EXT_RETURN_FAIL); \ + if (__rv==ECH_SAME_EXT_DONE) return(EXT_RETURN_SENT); \ + /* otherwise continue as normal */ \ + } + +#endif + +EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + /* Add RI if renegotiating */ + if (!s->renegotiate) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_renegotiate) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u8(pkt, s->s3.previous_client_finished, + s->s3.previous_client_finished_len) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +#ifndef OPENSSL_NO_ECH +/** + * @brief check which SNI to send when doing ECH + * @param s is the SSL context + * @return 1 for success + * + * An application can set inner and/or outer SNIs. + * Or it might only set one and we may have a + * public_name from an ECHConfig. + * Or an application may say to not send an outer + * or inner SNI at all. + * + * If the application states a preferece we'll + * abide by that, despite the public_name from + * an ECHConfig. + * + * This function fixes those up to ensure that + * the s->ext.hostname is as desired. + */ +static int ech_server_name_fixup(SSL *s) +{ + if (s->ech != NULL) { + char *pn=NULL; + size_t pn_len=0; + size_t in_len=0; + size_t on_len=0; + size_t ehn_len=0; + /* This from the ECHConfig */ + if (s->ech->cfg->recs!=NULL) { + if (s->ech->cfg->nrecs!=1) { + /* for now we only know how to handle one on the client */ + return(0); + } + pn_len=s->ech->cfg->recs[0].public_name_len; + pn=(char*)s->ech->cfg->recs[0].public_name; + } + /* These are from the application, direct */ + in_len=(s->ech->inner_name==NULL?0: + OPENSSL_strnlen(s->ech->inner_name,TLSEXT_MAXLEN_host_name)); + on_len=(s->ech->outer_name==NULL?0: + OPENSSL_strnlen(s->ech->outer_name,TLSEXT_MAXLEN_host_name)); + /* in cae there's a value set already (legacy app calls can do) */ + ehn_len=(s->ext.hostname==NULL?0: + OPENSSL_strnlen(s->ext.hostname,TLSEXT_MAXLEN_host_name)); + + if (s->ext.ch_depth==1) { /* Inner CH */ + if (in_len!=0) { + /* we prefer this over all */ + if (ehn_len!=0) { + OPENSSL_free(s->ext.hostname); + s->ext.hostname=NULL; + ehn_len=0; + } + s->ext.hostname=OPENSSL_strdup(s->ech->inner_name); + } + /* otherwise we leave the s->ext.hostname alone */ + } + + if (s->ext.ch_depth==0) { /* Outer CH */ + if (on_len!=0) { + if (ehn_len!=0) { + OPENSSL_free(s->ext.hostname); + s->ext.hostname=NULL; + ehn_len=0; + } + s->ext.hostname=OPENSSL_strdup(s->ech->outer_name); + } else if (pn_len!=0) { + if (ehn_len!=0) { + OPENSSL_free(s->ext.hostname); + s->ext.hostname=NULL; + ehn_len=0; + } + s->ext.hostname=OPENSSL_strndup(pn,pn_len); + } else { /* don't send possibly sensitive inner in outer! */ + if (ehn_len!=0) { + OPENSSL_free(s->ext.hostname); + s->ext.hostname=NULL; + ehn_len=0; + } + } + } + } + return 1; +} +#endif /* END OPENSSL_NO_ECH */ + +EXT_RETURN tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + +#ifndef OPENSSL_NO_ECH + if (s->ech != NULL) { + int echrv=0; + /* Don't send outer SNI if external API says that */ + if (s->ext.ch_depth==0 && + s->ech->outer_name==ECH_PUBLIC_NAME_OVERRIDE_NULL) { + return EXT_RETURN_NOT_SENT; + } + echrv=ech_server_name_fixup(s); + if (echrv!=1) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } +#endif + + if (s->ext.hostname == NULL) + return EXT_RETURN_NOT_SENT; + + /* Add TLS extension servername to the Client Hello message */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_name) + /* Sub-packet for server_name extension */ + || !WPACKET_start_sub_packet_u16(pkt) + /* Sub-packet for servername list (always 1 hostname)*/ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, TLSEXT_NAMETYPE_host_name) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.hostname, + strlen(s->ext.hostname)) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +/* Push a Max Fragment Len extension into ClientHello */ +EXT_RETURN tls_construct_ctos_maxfragmentlen(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->ext.max_fragment_len_mode == TLSEXT_max_fragment_length_DISABLED) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* Add Max Fragment Length extension if client enabled it. */ + /*- + * 4 bytes for this extension type and extension length + * 1 byte for the Max Fragment Length code value. + */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_max_fragment_length) + /* Sub-packet for Max Fragment Length extension (1 byte) */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, s->ext.max_fragment_len_mode) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +#ifndef OPENSSL_NO_SRP +EXT_RETURN tls_construct_ctos_srp(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + /* Add SRP username if there is one */ + if (s->srp_ctx.login == NULL) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_srp) + /* Sub-packet for SRP extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u8(pkt) + /* login must not be zero...internal error if so */ + || !WPACKET_set_flags(pkt, WPACKET_FLAGS_NON_ZERO_LENGTH) + || !WPACKET_memcpy(pkt, s->srp_ctx.login, + strlen(s->srp_ctx.login)) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif + +static int use_ecc(SSL *s, int min_version, int max_version) +{ + int i, end, ret = 0; + unsigned long alg_k, alg_a; + STACK_OF(SSL_CIPHER) *cipher_stack = NULL; + const uint16_t *pgroups = NULL; + size_t num_groups, j; + + /* See if we support any ECC ciphersuites */ + if (s->version == SSL3_VERSION) + return 0; + + cipher_stack = SSL_get1_supported_ciphers(s); + end = sk_SSL_CIPHER_num(cipher_stack); + for (i = 0; i < end; i++) { + const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); + + alg_k = c->algorithm_mkey; + alg_a = c->algorithm_auth; + if ((alg_k & (SSL_kECDHE | SSL_kECDHEPSK)) + || (alg_a & SSL_aECDSA) + || c->min_tls >= TLS1_3_VERSION) { + ret = 1; + break; + } + } + sk_SSL_CIPHER_free(cipher_stack); + if (!ret) + return 0; + + /* Check we have at least one EC supported group */ + tls1_get_supported_groups(s, &pgroups, &num_groups); + for (j = 0; j < num_groups; j++) { + uint16_t ctmp = pgroups[j]; + + if (tls_valid_group(s, ctmp, min_version, max_version, 1, NULL) + && tls_group_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED)) + return 1; + } + + return 0; +} + +EXT_RETURN tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + const unsigned char *pformats; + size_t num_formats; + int reason, min_version, max_version; + + reason = ssl_get_min_max_version(s, &min_version, &max_version, NULL); + if (reason != 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, reason); + return EXT_RETURN_FAIL; + } + if (!use_ecc(s, min_version, max_version)) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* Add TLS extension ECPointFormats to the ClientHello message */ + tls1_get_formatlist(s, &pformats, &num_formats); + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ec_point_formats) + /* Sub-packet for formats extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u8(pkt, pformats, num_formats) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +EXT_RETURN tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + const uint16_t *pgroups = NULL; + size_t num_groups = 0, i, tls13added = 0, added = 0; + int min_version, max_version, reason; + + reason = ssl_get_min_max_version(s, &min_version, &max_version, NULL); + if (reason != 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, reason); + return EXT_RETURN_FAIL; + } + + /* + * We only support EC groups in TLSv1.2 or below, and in DTLS. Therefore + * if we don't have EC support then we don't send this extension. + */ + if (!use_ecc(s, min_version, max_version) + && (SSL_IS_DTLS(s) || max_version < TLS1_3_VERSION)) + return EXT_RETURN_NOT_SENT; + +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* + * Add TLS extension supported_groups to the ClientHello message + */ + tls1_get_supported_groups(s, &pgroups, &num_groups); + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_groups) + /* Sub-packet for supported_groups extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_set_flags(pkt, WPACKET_FLAGS_NON_ZERO_LENGTH)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + /* Copy group ID if supported */ + for (i = 0; i < num_groups; i++) { + uint16_t ctmp = pgroups[i]; + int okfortls13; + + if (tls_valid_group(s, ctmp, min_version, max_version, 0, &okfortls13) + && tls_group_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED)) { +#ifndef OPENSSL_NO_TLS1_3 + int ctmp13 = ssl_group_id_internal_to_tls13(ctmp); + + if (ctmp13 != 0 && ctmp13 != ctmp + && max_version == TLS1_3_VERSION) { + if (!WPACKET_put_bytes_u16(pkt, ctmp13)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + tls13added++; + added++; + if (min_version == TLS1_3_VERSION) + continue; + } +#endif + if (!WPACKET_put_bytes_u16(pkt, ctmp)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (okfortls13 && max_version == TLS1_3_VERSION) + tls13added++; + added++; + } + } + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { + if (added == 0) + SSLfatal_data(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_GROUPS, + "No groups enabled for max supported SSL/TLS version"); + else + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + if (tls13added == 0 && max_version == TLS1_3_VERSION) { + SSLfatal_data(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_GROUPS, + "No groups enabled for max supported SSL/TLS version"); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +EXT_RETURN tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + size_t ticklen; + + if (!tls_use_ticket(s)) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!s->new_session && s->session != NULL + && s->session->ext.tick != NULL + && s->session->ssl_version != TLS1_3_VERSION) { + ticklen = s->session->ext.ticklen; + } else if (s->session && s->ext.session_ticket != NULL + && s->ext.session_ticket->data != NULL) { + ticklen = s->ext.session_ticket->length; + s->session->ext.tick = OPENSSL_malloc(ticklen); + if (s->session->ext.tick == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + memcpy(s->session->ext.tick, + s->ext.session_ticket->data, ticklen); + s->session->ext.ticklen = ticklen; + } else { + ticklen = 0; + } + + if (ticklen == 0 && s->ext.session_ticket != NULL && + s->ext.session_ticket->data == NULL) + return EXT_RETURN_NOT_SENT; + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_session_ticket) + || !WPACKET_sub_memcpy_u16(pkt, s->session->ext.tick, ticklen)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +EXT_RETURN tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + size_t salglen; + const uint16_t *salg; + + if (!SSL_CLIENT_USE_SIGALGS(s)) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + salglen = tls12_get_psigalgs(s, 1, &salg); + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signature_algorithms) + /* Sub-packet for sig-algs extension */ + || !WPACKET_start_sub_packet_u16(pkt) + /* Sub-packet for the actual list */ + || !WPACKET_start_sub_packet_u16(pkt) + || !tls12_copy_sigalgs(s, pkt, salg, salglen) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +#ifndef OPENSSL_NO_OCSP +EXT_RETURN tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + int i; + + /* This extension isn't defined for client Certificates */ + if (x != NULL) + return EXT_RETURN_NOT_SENT; + + if (s->ext.status_type != TLSEXT_STATUSTYPE_ocsp) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_status_request) + /* Sub-packet for status request extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, TLSEXT_STATUSTYPE_ocsp) + /* Sub-packet for the ids */ + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + for (i = 0; i < sk_OCSP_RESPID_num(s->ext.ocsp.ids); i++) { + unsigned char *idbytes; + OCSP_RESPID *id = sk_OCSP_RESPID_value(s->ext.ocsp.ids, i); + int idlen = i2d_OCSP_RESPID(id, NULL); + + if (idlen <= 0 + /* Sub-packet for an individual id */ + || !WPACKET_sub_allocate_bytes_u16(pkt, idlen, &idbytes) + || i2d_OCSP_RESPID(id, &idbytes) != idlen) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } + if (!WPACKET_close(pkt) + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (s->ext.ocsp.exts) { + unsigned char *extbytes; + int extlen = i2d_X509_EXTENSIONS(s->ext.ocsp.exts, NULL); + + if (extlen < 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (!WPACKET_allocate_bytes(pkt, extlen, &extbytes) + || i2d_X509_EXTENSIONS(s->ext.ocsp.exts, &extbytes) + != extlen) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif + +#ifndef OPENSSL_NO_NEXTPROTONEG +EXT_RETURN tls_construct_ctos_npn(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ctx->ext.npn_select_cb == NULL || !SSL_IS_FIRST_HANDSHAKE(s)) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* + * The client advertises an empty extension to indicate its support + * for Next Protocol Negotiation + */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_next_proto_neg) + || !WPACKET_put_bytes_u16(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif + +EXT_RETURN tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ +#ifndef OPENSSL_NO_ECH + unsigned char *aval=NULL; + size_t alen=0; +#endif + s->s3.alpn_sent = 0; + +#ifndef OPENSSL_NO_ECH + /* + * If we have different alpn and alpn_outer values, then we set + * the appropriate one for inner and outer. + * If no alpn is set (for inner or outer), we don't send any. + */ + if (!SSL_IS_FIRST_HANDSHAKE(s)) + return EXT_RETURN_NOT_SENT; + aval=s->ext.alpn; + alen=s->ext.alpn_len; + if (s->ext.ch_depth==1 && s->ext.alpn==NULL) /* inner */ + return EXT_RETURN_NOT_SENT; + if (s->ext.ch_depth==0 && !(s->ext.alpn || s->ext.alpn_outer)) /* outer */ + return EXT_RETURN_NOT_SENT; + if (s->ext.ch_depth==0 && s->ext.alpn_outer!=NULL) { + aval=s->ext.alpn_outer; + alen=s->ext.alpn_outer_len; + } + + if (!WPACKET_put_bytes_u16(pkt, + TLSEXT_TYPE_application_layer_protocol_negotiation) + /* Sub-packet ALPN extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u16(pkt, aval, alen) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + +#else + + if (s->ext.alpn == NULL || !SSL_IS_FIRST_HANDSHAKE(s)) + return EXT_RETURN_NOT_SENT; + + if (!WPACKET_put_bytes_u16(pkt, + TLSEXT_TYPE_application_layer_protocol_negotiation) + /* Sub-packet ALPN extension */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.alpn, s->ext.alpn_len) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } +#endif + s->s3.alpn_sent = 1; + + return EXT_RETURN_SENT; +} + + +#ifndef OPENSSL_NO_SRTP +EXT_RETURN tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = SSL_get_srtp_profiles(s); + int i, end; + + if (clnt == NULL) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_use_srtp) + /* Sub-packet for SRTP extension */ + || !WPACKET_start_sub_packet_u16(pkt) + /* Sub-packet for the protection profile list */ + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + end = sk_SRTP_PROTECTION_PROFILE_num(clnt); + for (i = 0; i < end; i++) { + const SRTP_PROTECTION_PROFILE *prof = + sk_SRTP_PROTECTION_PROFILE_value(clnt, i); + + if (prof == NULL || !WPACKET_put_bytes_u16(pkt, prof->id)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } + if (!WPACKET_close(pkt) + /* Add an empty use_mki value */ + || !WPACKET_put_bytes_u8(pkt, 0) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif + +EXT_RETURN tls_construct_ctos_etm(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->options & SSL_OP_NO_ENCRYPT_THEN_MAC) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_encrypt_then_mac) + || !WPACKET_put_bytes_u16(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +#ifndef OPENSSL_NO_CT +EXT_RETURN tls_construct_ctos_sct(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ct_validation_callback == NULL) + return EXT_RETURN_NOT_SENT; + + /* Not defined for client Certificates */ + if (x != NULL) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signed_certificate_timestamp) + || !WPACKET_put_bytes_u16(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif + +EXT_RETURN tls_construct_ctos_ems(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->options & SSL_OP_NO_EXTENDED_MASTER_SECRET) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_extended_master_secret) + || !WPACKET_put_bytes_u16(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +EXT_RETURN tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + int currv, min_version, max_version, reason; + + reason = ssl_get_min_max_version(s, &min_version, &max_version, NULL); + if (reason != 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, reason); + return EXT_RETURN_FAIL; + } + + /* + * Don't include this if we can't negotiate TLSv1.3. We can do a straight + * comparison here because we will never be called in DTLS. + */ + if (max_version < TLS1_3_VERSION) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_versions) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u8(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + for (currv = max_version; currv >= min_version; currv--) { + if (!WPACKET_put_bytes_u16(pkt, currv)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + +/* + * Construct a psk_kex_modes extension. + */ +EXT_RETURN tls_construct_ctos_psk_kex_modes(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + int nodhe = s->options & SSL_OP_ALLOW_NO_DHE_KEX; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_psk_kex_modes) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u8(pkt) + || !WPACKET_put_bytes_u8(pkt, TLSEXT_KEX_MODE_KE_DHE) + || (nodhe && !WPACKET_put_bytes_u8(pkt, TLSEXT_KEX_MODE_KE)) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + s->ext.psk_kex_mode = TLSEXT_KEX_MODE_FLAG_KE_DHE; + if (nodhe) + s->ext.psk_kex_mode |= TLSEXT_KEX_MODE_FLAG_KE; +#endif + + return EXT_RETURN_SENT; +} + +#ifndef OPENSSL_NO_TLS1_3 +static int add_key_share(SSL *s, WPACKET *pkt, unsigned int curve_id) +{ + unsigned char *encoded_point = NULL; + EVP_PKEY *key_share_key = NULL; + size_t encodedlen; + + if ( +#ifndef OPENSSL_NO_ECH + s->ech==NULL && /* with ECH a non-NULL, non-HRR tmp.pkey can be ok */ +#endif + s->s3.tmp.pkey != NULL) { + if (!ossl_assert(s->hello_retry_request == SSL_HRR_PENDING)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + /* + * Could happen if we got an HRR that wasn't requesting a new key_share + */ + key_share_key = s->s3.tmp.pkey; + } else { + key_share_key = ssl_generate_pkey_group(s, curve_id); + if (key_share_key == NULL) { + /* SSLfatal() already called */ + return 0; + } + } + + /* Encode the public key. */ + encodedlen = EVP_PKEY_get1_encoded_public_key(key_share_key, + &encoded_point); + if (encodedlen == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EC_LIB); + goto err; + } + + /* Create KeyShareEntry */ + if (!WPACKET_put_bytes_u16(pkt, ssl_group_id_internal_to_tls13(curve_id)) + || !WPACKET_sub_memcpy_u16(pkt, encoded_point, encodedlen)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* + * When changing to send more than one key_share we're + * going to need to be able to save more than one EVP_PKEY. For now + * we reuse the existing tmp.pkey + */ + s->s3.tmp.pkey = key_share_key; + s->s3.group_id = curve_id; + OPENSSL_free(encoded_point); + + return 1; + err: + if (s->s3.tmp.pkey == NULL) + EVP_PKEY_free(key_share_key); + OPENSSL_free(encoded_point); + return 0; +} +#endif + +EXT_RETURN tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + size_t i, num_groups = 0; + const uint16_t *pgroups = NULL; + uint16_t curve_id = 0; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* key_share extension */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share) + /* Extension data sub-packet */ + || !WPACKET_start_sub_packet_u16(pkt) + /* KeyShare list sub-packet */ + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + tls1_get_supported_groups(s, &pgroups, &num_groups); + + /* + * Make the number of key_shares sent configurable. For + * now, we just send one + */ + if (s->s3.group_id != 0) { + curve_id = s->s3.group_id; + } else { + for (i = 0; i < num_groups; i++) { + if (ssl_group_id_internal_to_tls13(pgroups[i]) == 0) + continue; + + if (!tls_group_allowed(s, pgroups[i], SSL_SECOP_CURVE_SUPPORTED)) + continue; + + curve_id = pgroups[i]; + break; + } + } + + if (curve_id == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_KEY_SHARE); + return EXT_RETURN_FAIL; + } + + if (!add_key_share(s, pkt, curve_id)) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +#else + return EXT_RETURN_NOT_SENT; +#endif +} + +EXT_RETURN tls_construct_ctos_cookie(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + EXT_RETURN ret = EXT_RETURN_FAIL; + + /* Should only be set if we've had an HRR */ + if (s->ext.tls13_cookie_len == 0) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_cookie) + /* Extension data sub-packet */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.tls13_cookie, + s->ext.tls13_cookie_len) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto end; + } + + ret = EXT_RETURN_SENT; + end: + OPENSSL_free(s->ext.tls13_cookie); + s->ext.tls13_cookie = NULL; + s->ext.tls13_cookie_len = 0; + + return ret; +} + +EXT_RETURN tls_construct_ctos_early_data(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ +#ifndef OPENSSL_NO_PSK + char identity[PSK_MAX_IDENTITY_LEN + 1]; +#endif /* OPENSSL_NO_PSK */ + const unsigned char *id = NULL; + size_t idlen = 0; + SSL_SESSION *psksess = NULL; + SSL_SESSION *edsess = NULL; + const EVP_MD *handmd = NULL; + +#ifndef OPENSSL_NO_ECH + /* + * Spec text: + * When the client offers the "early_data" extension in + * ClientHelloInner, it MUST also include the "early_data" extension + * in ClientHelloOuter. This allows servers that reject ECH and use + * ClientHelloOuter to safely ignore any early data sent by the + * client per [RFC8446], Section 4.2.10 + */ + if (s->ext.ch_depth==0 && + s->ext.inner_s!=NULL && + s->ext.inner_s->ext.early_data==SSL_EARLY_DATA_REJECTED && + s->ext.inner_s->ext.early_data_ok==1 + ) { + s->ext.early_data = SSL_EARLY_DATA_REJECTED; + s->ext.early_data_ok = 1; + s->psksession = s->ext.inner_s->psksession; + s->psksession_id = s->ext.inner_s->psksession_id; + s->max_early_data=s->ext.inner_s->max_early_data; + s->early_data_state=s->ext.inner_s->early_data_state; + } + IOSAME +#endif + + if (s->hello_retry_request == SSL_HRR_PENDING) + handmd = ssl_handshake_md(s); + + if (s->psk_use_session_cb != NULL + && (!s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess) + || (psksess != NULL + && psksess->ssl_version != TLS1_3_VERSION))) { + SSL_SESSION_free(psksess); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_PSK); + return EXT_RETURN_FAIL; + } + +#ifndef OPENSSL_NO_PSK + if (psksess == NULL && s->psk_client_callback != NULL) { + unsigned char psk[PSK_MAX_PSK_LEN]; + size_t psklen = 0; + + memset(identity, 0, sizeof(identity)); + psklen = s->psk_client_callback(s, NULL, identity, sizeof(identity) - 1, + psk, sizeof(psk)); + + if (psklen > PSK_MAX_PSK_LEN) { + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } else if (psklen > 0) { + const unsigned char tls13_aes128gcmsha256_id[] = { 0x13, 0x01 }; + const SSL_CIPHER *cipher; + + idlen = strlen(identity); + if (idlen > PSK_MAX_IDENTITY_LEN) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + id = (unsigned char *)identity; + + /* + * We found a PSK using an old style callback. We don't know + * the digest so we default to SHA256 as per the TLSv1.3 spec + */ + cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id); + if (cipher == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + psksess = SSL_SESSION_new(); + if (psksess == NULL + || !SSL_SESSION_set1_master_key(psksess, psk, psklen) + || !SSL_SESSION_set_cipher(psksess, cipher) + || !SSL_SESSION_set_protocol_version(psksess, TLS1_3_VERSION)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + OPENSSL_cleanse(psk, psklen); + return EXT_RETURN_FAIL; + } + OPENSSL_cleanse(psk, psklen); + } + } +#endif /* OPENSSL_NO_PSK */ + + SSL_SESSION_free(s->psksession); + s->psksession = psksess; + if (psksess != NULL) { + OPENSSL_free(s->psksession_id); + s->psksession_id = OPENSSL_memdup(id, idlen); + if (s->psksession_id == NULL) { + s->psksession_id_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + s->psksession_id_len = idlen; + } + + if (s->early_data_state != SSL_EARLY_DATA_CONNECTING + || (s->session->ext.max_early_data == 0 + && (psksess == NULL || psksess->ext.max_early_data == 0))) { + s->max_early_data = 0; + return EXT_RETURN_NOT_SENT; + } + edsess = s->session->ext.max_early_data != 0 ? s->session : psksess; + s->max_early_data = edsess->ext.max_early_data; + + if (edsess->ext.hostname != NULL) { + if (s->ext.hostname == NULL + || (s->ext.hostname != NULL + && strcmp(s->ext.hostname, edsess->ext.hostname) != 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_R_INCONSISTENT_EARLY_DATA_SNI); + return EXT_RETURN_FAIL; + } + } + + if ((s->ext.alpn == NULL && edsess->ext.alpn_selected != NULL)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_INCONSISTENT_EARLY_DATA_ALPN); + return EXT_RETURN_FAIL; + } + + /* + * Verify that we are offering an ALPN protocol consistent with the early + * data. + */ + if (edsess->ext.alpn_selected != NULL) { + PACKET prots, alpnpkt; + int found = 0; + + if (!PACKET_buf_init(&prots, s->ext.alpn, s->ext.alpn_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + while (PACKET_get_length_prefixed_1(&prots, &alpnpkt)) { + if (PACKET_equal(&alpnpkt, edsess->ext.alpn_selected, + edsess->ext.alpn_selected_len)) { + found = 1; + break; + } + } + if (!found) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_R_INCONSISTENT_EARLY_DATA_ALPN); + return EXT_RETURN_FAIL; + } + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + /* + * We set this to rejected here. Later, if the server acknowledges the + * extension, we set it to accepted. + */ + s->ext.early_data = SSL_EARLY_DATA_REJECTED; + s->ext.early_data_ok = 1; + + return EXT_RETURN_SENT; +} + +#define F5_WORKAROUND_MIN_MSG_LEN 0xff +#define F5_WORKAROUND_MAX_MSG_LEN 0x200 + +/* + * PSK pre binder overhead = + * 2 bytes for TLSEXT_TYPE_psk + * 2 bytes for extension length + * 2 bytes for identities list length + * 2 bytes for identity length + * 4 bytes for obfuscated_ticket_age + * 2 bytes for binder list length + * 1 byte for binder length + * The above excludes the number of bytes for the identity itself and the + * subsequent binder bytes + */ +#define PSK_PRE_BINDER_OVERHEAD (2 + 2 + 2 + 2 + 4 + 2 + 1) + +EXT_RETURN tls_construct_ctos_padding(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + unsigned char *padbytes; + size_t hlen; + +#ifndef OPENSSL_NO_ECH + if (s->ext.ch_depth==1 && + s->ext.ech_attempted_type==ECH_DRAFT_13_VERSION && + (s->options & SSL_OP_TLSEXT_PADDING) == 0) { + /* draft-13 pads outside the encoded inner */ + return EXT_RETURN_NOT_SENT; + } + if (!(s->ext.ech_grease==ECH_IS_GREASE) && + !(s->ech && s->ext.ch_depth==1) && + (s->options & SSL_OP_TLSEXT_PADDING) == 0) + return EXT_RETURN_NOT_SENT; +#else + if ((s->options & SSL_OP_TLSEXT_PADDING) == 0) + return EXT_RETURN_NOT_SENT; +#endif + + /* + * Add padding to workaround bugs in F5 terminators. See RFC7685. + * This code calculates the length of all extensions added so far but + * excludes the PSK extension (because that MUST be written last). Therefore + * this extension MUST always appear second to last. + */ + if (!WPACKET_get_total_written(pkt, &hlen)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + /* + * If we're going to send a PSK then that will be written out after this + * extension, so we need to calculate how long it is going to be. + */ + if (s->session->ssl_version == TLS1_3_VERSION + && s->session->ext.ticklen != 0 + && s->session->cipher != NULL) { + const EVP_MD *md = ssl_md(s->ctx, s->session->cipher->algorithm2); + + if (md != NULL) { + /* + * Add the fixed PSK overhead, the identity length and the binder + * length. + */ + hlen += PSK_PRE_BINDER_OVERHEAD + s->session->ext.ticklen + + EVP_MD_get_size(md); + } + } + + if (hlen > F5_WORKAROUND_MIN_MSG_LEN && hlen < F5_WORKAROUND_MAX_MSG_LEN) { + /* Calculate the amount of padding we need to add */ + hlen = F5_WORKAROUND_MAX_MSG_LEN - hlen; + /* Super large padding for circumvention of non-reassembling boxes */ + if (s->options & SSL_OP_TLSEXT_PADDING_SUPER) { + RAND_bytes(&hlen, sizeof(hlen)); + hlen = 2000 + (hlen % 10000); + } + + /* + * Take off the size of extension header itself (2 bytes for type and + * 2 bytes for length bytes), but ensure that the extension is at least + * 1 byte long so as not to have an empty extension last (WebSphere 7.x, + * 8.x are intolerant of that condition) + */ + if (hlen > 4) + hlen -= 4; + else + hlen = 1; + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_padding) + || !WPACKET_sub_allocate_bytes_u16(pkt, hlen, &padbytes)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + memset(padbytes, 0, hlen); + } + + return EXT_RETURN_SENT; +} + +/* + * Construct the pre_shared_key extension + */ +EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + uint32_t now, agesec, agems = 0; + size_t reshashsize = 0, pskhashsize = 0, binderoffset, msglen; + unsigned char *resbinder = NULL, *pskbinder = NULL, *msgstart = NULL; + const EVP_MD *handmd = NULL, *mdres = NULL, *mdpsk = NULL; + int dores = 0; + + s->ext.tick_identity = 0; + + /* + * Note: At this stage of the code we only support adding a single + * resumption PSK. If we add support for multiple PSKs then the length + * calculations in the padding extension will need to be adjusted. + */ + + /* + * If this is an incompatible or new session then we have nothing to resume + * so don't add this extension. + */ + if (s->session->ssl_version != TLS1_3_VERSION + || (s->session->ext.ticklen == 0 && s->psksession == NULL)) + return EXT_RETURN_NOT_SENT; + + if (s->hello_retry_request == SSL_HRR_PENDING) + handmd = ssl_handshake_md(s); + + if (s->session->ext.ticklen != 0) { + /* Get the digest associated with the ciphersuite in the session */ + if (s->session->cipher == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + mdres = ssl_md(s->ctx, s->session->cipher->algorithm2); + if (mdres == NULL) { + /* + * Don't recognize this cipher so we can't use the session. + * Ignore it + */ + goto dopsksess; + } + + if (s->hello_retry_request == SSL_HRR_PENDING && mdres != handmd) { + /* + * Selected ciphersuite hash does not match the hash for the session + * so we can't use it. + */ + goto dopsksess; + } + + /* + * Technically the C standard just says time() returns a time_t and says + * nothing about the encoding of that type. In practice most + * implementations follow POSIX which holds it as an integral type in + * seconds since epoch. We've already made the assumption that we can do + * this in multiple places in the code, so portability shouldn't be an + * issue. + */ + now = (uint32_t)time(NULL); + agesec = now - (uint32_t)s->session->time; + /* + * We calculate the age in seconds but the server may work in ms. Due to + * rounding errors we could overestimate the age by up to 1s. It is + * better to underestimate it. Otherwise, if the RTT is very short, when + * the server calculates the age reported by the client it could be + * bigger than the age calculated on the server - which should never + * happen. + */ + if (agesec > 0) + agesec--; + + if (s->session->ext.tick_lifetime_hint < agesec) { + /* Ticket is too old. Ignore it. */ + goto dopsksess; + } + + /* + * Calculate age in ms. We're just doing it to nearest second. Should be + * good enough. + */ + agems = agesec * (uint32_t)1000; + + if (agesec != 0 && agems / (uint32_t)1000 != agesec) { + /* + * Overflow. Shouldn't happen unless this is a *really* old session. + * If so we just ignore it. + */ + goto dopsksess; + } + + /* + * Obfuscate the age. Overflow here is fine, this addition is supposed + * to be mod 2^32. + */ + agems += s->session->ext.tick_age_add; + + reshashsize = EVP_MD_get_size(mdres); + s->ext.tick_identity++; + dores = 1; + } + + dopsksess: + if (!dores && s->psksession == NULL) + return EXT_RETURN_NOT_SENT; + + if (s->psksession != NULL) { + mdpsk = ssl_md(s->ctx, s->psksession->cipher->algorithm2); + if (mdpsk == NULL) { + /* + * Don't recognize this cipher so we can't use the session. + * If this happens it's an application bug. + */ + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_PSK); + return EXT_RETURN_FAIL; + } + + if (s->hello_retry_request == SSL_HRR_PENDING && mdpsk != handmd) { + /* + * Selected ciphersuite hash does not match the hash for the PSK + * session. This is an application bug. + */ + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_PSK); + return EXT_RETURN_FAIL; + } + + pskhashsize = EVP_MD_get_size(mdpsk); + } + + /* Create the extension, but skip over the binder for now */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_psk) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + +#ifndef OPENSSL_NO_ECH + /* + * For ECH if we're processing the outer CH and the inner CH + * has a PSK, then we want to send a GREASE PSK in the outer. + * We'll do that by just replacing the ticket value itself + * with a random value of the same length. + */ + { + unsigned char *ltick=s->session->ext.tick; + unsigned char *rndbuf=NULL; + + if (s->ech && s->ext.ch_depth==0) { /* outer CH */ + /* allocate a similar sized random value */ + rndbuf=OPENSSL_malloc(s->session->ext.ticklen); + if (!rndbuf) return EXT_RETURN_FAIL; + if (RAND_bytes_ex(s->ctx->libctx, rndbuf, + s->session->ext.ticklen, RAND_DRBG_STRENGTH) <= 0) { + OPENSSL_free(rndbuf); + return EXT_RETURN_FAIL; + } + ltick=rndbuf; + } + + if (dores) { + if (!WPACKET_sub_memcpy_u16(pkt, ltick, + s->session->ext.ticklen) + || !WPACKET_put_bytes_u32(pkt, agems)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + if (rndbuf!=NULL) OPENSSL_free(rndbuf); + return EXT_RETURN_FAIL; + } + } + + if (s->psksession != NULL) { + if (!WPACKET_sub_memcpy_u16(pkt, s->psksession_id, + s->psksession_id_len) + || !WPACKET_put_bytes_u32(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + if (rndbuf!=NULL) OPENSSL_free(rndbuf); + return EXT_RETURN_FAIL; + } + s->ext.tick_identity++; + } + + if (!WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &binderoffset) + || !WPACKET_start_sub_packet_u16(pkt) + || (dores + && !WPACKET_sub_allocate_bytes_u8(pkt, reshashsize, + &resbinder)) + || (s->psksession != NULL + && !WPACKET_sub_allocate_bytes_u8(pkt, pskhashsize, + &pskbinder)) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &msglen) + /* + * We need to fill in all the sub-packet lengths now so we can + * calculate the HMAC of the message up to the binders + */ + || !WPACKET_fill_lengths(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + if (rndbuf!=NULL) OPENSSL_free(rndbuf); + return EXT_RETURN_FAIL; + } + + if (rndbuf!=NULL) OPENSSL_free(rndbuf); + + } +#else + + if (dores) { + if (!WPACKET_sub_memcpy_u16(pkt, s->session->ext.tick, + s->session->ext.ticklen) + || !WPACKET_put_bytes_u32(pkt, agems)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + } + + if (s->psksession != NULL) { + if (!WPACKET_sub_memcpy_u16(pkt, s->psksession_id, + s->psksession_id_len) + || !WPACKET_put_bytes_u32(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + s->ext.tick_identity++; + } + + if (!WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &binderoffset) + || !WPACKET_start_sub_packet_u16(pkt) + || (dores + && !WPACKET_sub_allocate_bytes_u8(pkt, reshashsize, &resbinder)) + || (s->psksession != NULL + && !WPACKET_sub_allocate_bytes_u8(pkt, pskhashsize, &pskbinder)) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &msglen) + /* + * We need to fill in all the sub-packet lengths now so we can + * calculate the HMAC of the message up to the binders + */ + || !WPACKET_fill_lengths(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + +#endif /* OPENSSL_NO_ECH */ + + msgstart = WPACKET_get_curr(pkt) - msglen; + + if (dores + && tls_psk_do_binder(s, mdres, msgstart, binderoffset, NULL, + resbinder, s->session, 1, 0) != 1) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + + if (s->psksession != NULL + && tls_psk_do_binder(s, mdpsk, msgstart, binderoffset, NULL, + pskbinder, s->psksession, 1, 1) != 1) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +#else + return EXT_RETURN_NOT_SENT; +#endif +} + +EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, + ossl_unused unsigned int context, + ossl_unused X509 *x, + ossl_unused size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + if (!s->pha_enabled) + return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_ECH + IOSAME +#endif + + /* construct extension - 0 length, no contents */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_post_handshake_auth) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + s->post_handshake_auth = SSL_PHA_EXT_SENT; + + return EXT_RETURN_SENT; +#else + return EXT_RETURN_NOT_SENT; +#endif +} + + +/* + * Parse the server's renegotiation binding and abort if it's not right + */ +int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + size_t expected_len = s->s3.previous_client_finished_len + + s->s3.previous_server_finished_len; + size_t ilen; + const unsigned char *data; + + /* Check for logic errors */ + if (!ossl_assert(expected_len == 0 + || s->s3.previous_client_finished_len != 0) + || !ossl_assert(expected_len == 0 + || s->s3.previous_server_finished_len != 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Parse the length byte */ + if (!PACKET_get_1_len(pkt, &ilen)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_RENEGOTIATION_ENCODING_ERR); + return 0; + } + + /* Consistency check */ + if (PACKET_remaining(pkt) != ilen) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_RENEGOTIATION_ENCODING_ERR); + return 0; + } + + /* Check that the extension matches */ + if (ilen != expected_len) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_RENEGOTIATION_MISMATCH); + return 0; + } + + if (!PACKET_get_bytes(pkt, &data, s->s3.previous_client_finished_len) + || memcmp(data, s->s3.previous_client_finished, + s->s3.previous_client_finished_len) != 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_RENEGOTIATION_MISMATCH); + return 0; + } + + if (!PACKET_get_bytes(pkt, &data, s->s3.previous_server_finished_len) + || memcmp(data, s->s3.previous_server_finished, + s->s3.previous_server_finished_len) != 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_RENEGOTIATION_MISMATCH); + return 0; + } + s->s3.send_connection_binding = 1; + + return 1; +} + +/* Parse the server's max fragment len extension packet */ +int tls_parse_stoc_maxfragmentlen(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + unsigned int value; + + if (PACKET_remaining(pkt) != 1 || !PACKET_get_1(pkt, &value)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + /* |value| should contains a valid max-fragment-length code. */ + if (!IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH); + return 0; + } + + /* Must be the same value as client-configured one who was sent to server */ + /*- + * RFC 6066: if a client receives a maximum fragment length negotiation + * response that differs from the length it requested, ... + * It must abort with SSL_AD_ILLEGAL_PARAMETER alert + */ + if (value != s->ext.max_fragment_len_mode) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH); + return 0; + } + + /* + * Maximum Fragment Length Negotiation succeeded. + * The negotiated Maximum Fragment Length is binding now. + */ + s->session->ext.max_fragment_len_mode = value; + + return 1; +} + +int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ext.hostname == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (PACKET_remaining(pkt) > 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + if (!s->hit) { + if (s->session->ext.hostname != NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + s->session->ext.hostname = OPENSSL_strdup(s->ext.hostname); + if (s->session->ext.hostname == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + } + + return 1; +} + +int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + size_t ecpointformats_len; + PACKET ecptformatlist; + + if (!PACKET_as_length_prefixed_1(pkt, &ecptformatlist)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + if (!s->hit) { + ecpointformats_len = PACKET_remaining(&ecptformatlist); + if (ecpointformats_len == 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH); + return 0; + } + + s->ext.peer_ecpointformats_len = 0; + OPENSSL_free(s->ext.peer_ecpointformats); + s->ext.peer_ecpointformats = OPENSSL_malloc(ecpointformats_len); + if (s->ext.peer_ecpointformats == NULL) { + s->ext.peer_ecpointformats_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + s->ext.peer_ecpointformats_len = ecpointformats_len; + + if (!PACKET_copy_bytes(&ecptformatlist, + s->ext.peer_ecpointformats, + ecpointformats_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + } + + return 1; +} + +int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ext.session_ticket_cb != NULL && + !s->ext.session_ticket_cb(s, PACKET_data(pkt), + PACKET_remaining(pkt), + s->ext.session_ticket_cb_arg)) { + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_EXTENSION); + return 0; + } + + if (!tls_use_ticket(s)) { + SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); + return 0; + } + if (PACKET_remaining(pkt) > 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + s->ext.ticket_expected = 1; + + return 1; +} + +#ifndef OPENSSL_NO_OCSP +int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (context == SSL_EXT_TLS1_3_CERTIFICATE_REQUEST) { + /* We ignore this if the server sends a CertificateRequest */ + return 1; + } + + /* + * MUST only be sent if we've requested a status + * request message. In TLS <= 1.2 it must also be empty. + */ + if (s->ext.status_type != TLSEXT_STATUSTYPE_ocsp) { + SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); + return 0; + } + if (!SSL_IS_TLS13(s) && PACKET_remaining(pkt) > 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + if (SSL_IS_TLS13(s)) { + /* We only know how to handle this if it's for the first Certificate in + * the chain. We ignore any other responses. + */ + if (chainidx != 0) + return 1; + + /* SSLfatal() already called */ + return tls_process_cert_status_body(s, pkt); + } + + /* Set flag to expect CertificateStatus message */ + s->ext.status_expected = 1; + + return 1; +} +#endif + + +#ifndef OPENSSL_NO_CT +int tls_parse_stoc_sct(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + if (context == SSL_EXT_TLS1_3_CERTIFICATE_REQUEST) { + /* We ignore this if the server sends it in a CertificateRequest */ + return 1; + } + + /* + * Only take it if we asked for it - i.e if there is no CT validation + * callback set, then a custom extension MAY be processing it, so we + * need to let control continue to flow to that. + */ + if (s->ct_validation_callback != NULL) { + size_t size = PACKET_remaining(pkt); + + /* Simply copy it off for later processing */ + OPENSSL_free(s->ext.scts); + s->ext.scts = NULL; + + s->ext.scts_len = (uint16_t)size; + if (size > 0) { + s->ext.scts = OPENSSL_malloc(size); + if (s->ext.scts == NULL) { + s->ext.scts_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!PACKET_copy_bytes(pkt, s->ext.scts, size)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + } + } else { + ENDPOINT role = (context & SSL_EXT_TLS1_2_SERVER_HELLO) != 0 + ? ENDPOINT_CLIENT : ENDPOINT_BOTH; + + /* + * If we didn't ask for it then there must be a custom extension, + * otherwise this is unsolicited. + */ + if (custom_ext_find(&s->cert->custext, role, + TLSEXT_TYPE_signed_certificate_timestamp, + NULL) == NULL) { + SSLfatal(s, TLS1_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); + return 0; + } + + if (!custom_ext_parse(s, context, + TLSEXT_TYPE_signed_certificate_timestamp, + PACKET_data(pkt), PACKET_remaining(pkt), + x, chainidx)) { + /* SSLfatal already called */ + return 0; + } + } + + return 1; +} +#endif + + +#ifndef OPENSSL_NO_NEXTPROTONEG +/* + * ssl_next_proto_validate validates a Next Protocol Negotiation block. No + * elements of zero length are allowed and the set of elements must exactly + * fill the length of the block. Returns 1 on success or 0 on failure. + */ +static int ssl_next_proto_validate(SSL *s, PACKET *pkt) +{ + PACKET tmp_protocol; + + while (PACKET_remaining(pkt)) { + if (!PACKET_get_length_prefixed_1(pkt, &tmp_protocol) + || PACKET_remaining(&tmp_protocol) == 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + } + + return 1; +} + +int tls_parse_stoc_npn(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + unsigned char *selected; + unsigned char selected_len; + PACKET tmppkt; + + /* Check if we are in a renegotiation. If so ignore this extension */ + if (!SSL_IS_FIRST_HANDSHAKE(s)) + return 1; + + /* We must have requested it. */ + if (s->ctx->ext.npn_select_cb == NULL) { + SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); + return 0; + } + + /* The data must be valid */ + tmppkt = *pkt; + if (!ssl_next_proto_validate(s, &tmppkt)) { + /* SSLfatal() already called */ + return 0; + } + if (s->ctx->ext.npn_select_cb(s, &selected, &selected_len, + PACKET_data(pkt), + PACKET_remaining(pkt), + s->ctx->ext.npn_select_cb_arg) != + SSL_TLSEXT_ERR_OK) { + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_EXTENSION); + return 0; + } + + /* + * Could be non-NULL if server has sent multiple NPN extensions in + * a single Serverhello + */ + OPENSSL_free(s->ext.npn); + s->ext.npn = OPENSSL_malloc(selected_len); + if (s->ext.npn == NULL) { + s->ext.npn_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + memcpy(s->ext.npn, selected, selected_len); + s->ext.npn_len = selected_len; + s->s3.npn_seen = 1; + + return 1; +} +#endif + +int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + size_t len; + + /* We must have requested it. */ + if (!s->s3.alpn_sent) { + SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); + return 0; + } + /*- + * The extension data consists of: + * uint16 list_length + * uint8 proto_length; + * uint8 proto[proto_length]; + */ + if (!PACKET_get_net_2_len(pkt, &len) + || PACKET_remaining(pkt) != len || !PACKET_get_1_len(pkt, &len) + || PACKET_remaining(pkt) != len) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + OPENSSL_free(s->s3.alpn_selected); + s->s3.alpn_selected = OPENSSL_malloc(len); + if (s->s3.alpn_selected == NULL) { + s->s3.alpn_selected_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + if (!PACKET_copy_bytes(pkt, s->s3.alpn_selected, len)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + s->s3.alpn_selected_len = len; + + if (s->session->ext.alpn_selected == NULL + || s->session->ext.alpn_selected_len != len + || memcmp(s->session->ext.alpn_selected, s->s3.alpn_selected, len) + != 0) { + /* ALPN not consistent with the old session so cannot use early_data */ + s->ext.early_data_ok = 0; + } + if (!s->hit) { + /* + * This is a new session and so alpn_selected should have been + * initialised to NULL. We should update it with the selected ALPN. + */ + if (!ossl_assert(s->session->ext.alpn_selected == NULL)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + s->session->ext.alpn_selected = + OPENSSL_memdup(s->s3.alpn_selected, s->s3.alpn_selected_len); + if (s->session->ext.alpn_selected == NULL) { + s->session->ext.alpn_selected_len = 0; + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + s->session->ext.alpn_selected_len = s->s3.alpn_selected_len; + } + + return 1; +} + +#ifndef OPENSSL_NO_SRTP +int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + unsigned int id, ct, mki; + int i; + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt; + SRTP_PROTECTION_PROFILE *prof; + + if (!PACKET_get_net_2(pkt, &ct) || ct != 2 + || !PACKET_get_net_2(pkt, &id) + || !PACKET_get_1(pkt, &mki) + || PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + + if (mki != 0) { + /* Must be no MKI, since we never offer one */ + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_SRTP_MKI_VALUE); + return 0; + } + + /* Throw an error if the server gave us an unsolicited extension */ + clnt = SSL_get_srtp_profiles(s); + if (clnt == NULL) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_NO_SRTP_PROFILES); + return 0; + } + + /* + * Check to see if the server gave us something we support (and + * presumably offered) + */ + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(clnt); i++) { + prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i); + + if (prof->id == id) { + s->srtp_profile = prof; + return 1; + } + } + + SSLfatal(s, SSL_AD_DECODE_ERROR, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; +} +#endif + +int tls_parse_stoc_etm(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + /* Ignore if inappropriate ciphersuite */ + if (!(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC) + && s->s3.tmp.new_cipher->algorithm_mac != SSL_AEAD + && s->s3.tmp.new_cipher->algorithm_enc != SSL_RC4 + && s->s3.tmp.new_cipher->algorithm_enc != SSL_eGOST2814789CNT + && s->s3.tmp.new_cipher->algorithm_enc != SSL_eGOST2814789CNT12 + && s->s3.tmp.new_cipher->algorithm_enc != SSL_MAGMA + && s->s3.tmp.new_cipher->algorithm_enc != SSL_KUZNYECHIK) + s->ext.use_etm = 1; + + return 1; +} + +int tls_parse_stoc_ems(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->options & SSL_OP_NO_EXTENDED_MASTER_SECRET) + return 1; + s->s3.flags |= TLS1_FLAGS_RECEIVED_EXTMS; + if (!s->hit) + s->session->flags |= SSL_SESS_FLAG_EXTMS; + + return 1; +} + +int tls_parse_stoc_supported_versions(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + unsigned int version; + + if (!PACKET_get_net_2(pkt, &version) + || PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + /* + * The only protocol version we support which is valid in this extension in + * a ServerHello is TLSv1.3 therefore we shouldn't be getting anything else. + */ + if (version != TLS1_3_VERSION) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_R_BAD_PROTOCOL_VERSION_NUMBER); + return 0; + } + + /* We ignore this extension for HRRs except to sanity check it */ + if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) + return 1; + + /* We just set it here. We validate it in ssl_choose_client_version */ + s->version = version; + + return 1; +} + +int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + unsigned int group_id; + PACKET encoded_pt; + EVP_PKEY *ckey = s->s3.tmp.pkey, *skey = NULL; + const TLS_GROUP_INFO *ginf = NULL; + + /* Sanity check */ + if (ckey == NULL || s->s3.peer_tmp != NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!PACKET_get_net_2(pkt, &group_id)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + group_id = ssl_group_id_tls13_to_internal(group_id); + if ((context & SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) != 0) { + const uint16_t *pgroups = NULL; + size_t i, num_groups; + + if (PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + /* + * It is an error if the HelloRetryRequest wants a key_share that we + * already sent in the first ClientHello + */ + if (group_id == s->s3.group_id) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + +#ifndef OPENSSL_NO_ECH + /* + * if we tried ECH and got HRR then we need to fix/check + * group in inner and outer as well + */ + if (s->ech && s->ext.outer_s!=NULL) { + if (group_id == s->ext.outer_s->s3.group_id) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + s->ext.outer_s->s3.group_id=group_id; + } + if (s->ech && s->ext.inner_s!=NULL) { + if (group_id == s->ext.inner_s->s3.group_id) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + s->ext.inner_s->s3.group_id=group_id; + } +#endif + + /* Validate the selected group is one we support */ + tls1_get_supported_groups(s, &pgroups, &num_groups); + for (i = 0; i < num_groups; i++) { + if (group_id == pgroups[i]) + break; + } + if (i >= num_groups + || !tls_group_allowed(s, group_id, SSL_SECOP_CURVE_SUPPORTED)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + + s->s3.group_id = group_id; + EVP_PKEY_free(s->s3.tmp.pkey); + s->s3.tmp.pkey = NULL; + return 1; + } + + if (group_id != s->s3.group_id) { + /* + * This isn't for the group that we sent in the original + * key_share! + */ + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + /* Retain this group in the SSL_SESSION */ + if (!s->hit) { + s->session->kex_group = group_id; + } else if (group_id != s->session->kex_group) { + /* + * If this is a resumption but changed what group was used, we need + * to record the new group in the session, but the session is not + * a new session and could be in use by other threads. So, make + * a copy of the session to record the new information so that it's + * useful for any sessions resumed from tickets issued on this + * connection. + */ + SSL_SESSION *new_sess; + + if ((new_sess = ssl_session_dup(s->session, 0)) == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE); + return 0; + } + SSL_SESSION_free(s->session); + s->session = new_sess; + s->session->kex_group = group_id; + } + + if ((ginf = tls1_group_id_lookup(s->ctx, group_id)) == NULL) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE); + return 0; + } + + if (!PACKET_as_length_prefixed_2(pkt, &encoded_pt) + || PACKET_remaining(&encoded_pt) == 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + if (!ginf->is_kem) { + /* Regular KEX */ + skey = EVP_PKEY_new(); + if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_COPY_PARAMETERS_FAILED); + EVP_PKEY_free(skey); + return 0; + } + + if (tls13_set_encoded_pub_key(skey, PACKET_data(&encoded_pt), + PACKET_remaining(&encoded_pt)) <= 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_ECPOINT); + EVP_PKEY_free(skey); + return 0; + } + + if (ssl_derive(s, ckey, skey, 1) == 0) { + /* SSLfatal() already called */ + EVP_PKEY_free(skey); + return 0; + } + s->s3.peer_tmp = skey; + } else { + /* KEM Mode */ + const unsigned char *ct = PACKET_data(&encoded_pt); + size_t ctlen = PACKET_remaining(&encoded_pt); + + if (ssl_decapsulate(s, ckey, ct, ctlen, 1) == 0) { + /* SSLfatal() already called */ + return 0; + } + } + s->s3.did_kex = 1; +#endif + + return 1; +} + +int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + PACKET cookie; + + if (!PACKET_as_length_prefixed_2(pkt, &cookie) + || !PACKET_memdup(&cookie, &s->ext.tls13_cookie, + &s->ext.tls13_cookie_len)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + return 1; +} + +int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) { + unsigned long max_early_data; + + if (!PACKET_get_net_4(pkt, &max_early_data) + || PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_INVALID_MAX_EARLY_DATA); + return 0; + } + + s->session->ext.max_early_data = max_early_data; + + return 1; + } + + if (PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + if (!s->ext.early_data_ok + || !s->hit) { + /* + * If we get here then we didn't send early data, or we didn't resume + * using the first identity, or the SNI/ALPN is not consistent so the + * server should not be accepting it. + */ + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_EXTENSION); + return 0; + } + + s->ext.early_data = SSL_EARLY_DATA_ACCEPTED; + + return 1; +} + +int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + unsigned int identity; + + if (!PACKET_get_net_2(pkt, &identity) || PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + + if (identity >= (unsigned int)s->ext.tick_identity) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_PSK_IDENTITY); + return 0; + } + + /* + * Session resumption tickets are always sent before PSK tickets. If the + * ticket index is 0 then it must be for a session resumption ticket if we + * sent two tickets, or if we didn't send a PSK ticket. + */ + if (identity == 0 && (s->psksession == NULL || s->ext.tick_identity == 2)) { + s->hit = 1; + SSL_SESSION_free(s->psksession); + s->psksession = NULL; + return 1; + } + + if (s->psksession == NULL) { + /* Should never happen */ + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* + * If we used the external PSK for sending early_data then s->early_secret + * is already set up, so don't overwrite it. Otherwise we copy the + * early_secret across that we generated earlier. + */ + if ((s->early_data_state != SSL_EARLY_DATA_WRITE_RETRY + && s->early_data_state != SSL_EARLY_DATA_FINISHED_WRITING) + || s->session->ext.max_early_data > 0 + || s->psksession->ext.max_early_data == 0) + memcpy(s->early_secret, s->psksession->early_secret, EVP_MAX_MD_SIZE); + + SSL_SESSION_free(s->session); + s->session = s->psksession; + s->psksession = NULL; + s->hit = 1; + /* Early data is only allowed if we used the first ticket */ + if (identity != 0) + s->ext.early_data_ok = 0; +#endif + + return 1; +} + +#ifndef OPENSSL_NO_ECH + +/** + * @brief Create the draft-10 ECH extension for the ClientHello + */ +EXT_RETURN tls_construct_ctos_ech(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ext.ech_attempted_type==TLSEXT_TYPE_ech && + (s->ext.ech_grease==ECH_IS_GREASE || (s->options & SSL_OP_ECH_GREASE))) { + if (s->hello_retry_request==SSL_HRR_PENDING && + s->ext.ech_sent!=NULL) { + /* re-tx already sent GREASEy ECH*/ + if (WPACKET_memcpy(pkt,s->ext.ech_sent,s->ext.ech_sent_len)!=1) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + return EXT_RETURN_SENT; + } + if (ech_send_grease(s,pkt)!=1) { + return EXT_RETURN_NOT_SENT; + } + return EXT_RETURN_SENT; + } + /* + * We'll fake out sending this one - after the entire thing has been + * constructed we'll then finally encode and encrypt - need to do it + * that way as we need the rest of the outer CH as AAD input to the + * encryption (sigh!) + */ + return EXT_RETURN_NOT_SENT; +} + +/** + * @brief Create the draft-13 ECH extension for the ClientHello + */ +EXT_RETURN tls_construct_ctos_ech13(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (s->ext.ech_attempted_type!=TLSEXT_TYPE_ech13) + return EXT_RETURN_NOT_SENT; + + /* don't send grease if really attempting ECH */ + if (s->ext.ech_attempted==0) { + if (s->ext.ech_grease==ECH_IS_GREASE || + (s->options & SSL_OP_ECH_GREASE)) { + if (s->hello_retry_request==SSL_HRR_PENDING && + s->ext.ech_sent!=NULL) { + /* re-tx already sent GREASEy ECH*/ + if (WPACKET_memcpy(pkt,s->ext.ech_sent, + s->ext.ech_sent_len)!=1) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + return EXT_RETURN_SENT; + } + if (ech_send_grease(s,pkt)!=1) { + return EXT_RETURN_NOT_SENT; + } + return EXT_RETURN_SENT; + } + } + /* + * We fake out sending the outer value - after the entire thing has been + * constructed we only then finally encode and encrypt - need to do it + * that way as we need the rest of the outer CH as AAD input to the + * encryption. + */ + if (s->ext.ch_depth==0) return EXT_RETURN_NOT_SENT; + /* For the inner CH - we simply include one of these saying "inner" */ + if (s->ext.ch_depth==1) { + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech13) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, ECH_INNER_CH_TYPE) + || !WPACKET_close(pkt) + ) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + return EXT_RETURN_SENT; + } + + return EXT_RETURN_FAIL; +} + +/** + * @brief if the server thinks we GREASE'd then we may get an ECHConfigList + * + * A similar thing can happen with an HRR confirmation, but we don't + * really process that here. + */ +int tls_parse_stoc_ech(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + unsigned int rlen=0; + const unsigned char *rval=NULL; + unsigned char *srval=NULL; + if (context==SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) { + /* + * The HRR will have an ECH extension with the + * 8-octet confirmation value but it's processed + * elsewhere, so just return ok. + */ + return 1; + } + if (!PACKET_get_net_2(pkt, &rlen)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); + return 0; + } + if (!PACKET_get_bytes(pkt, &rval, rlen)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_LENGTH_MISMATCH); + return 0; + } + if (s->ext.ech_returned) OPENSSL_free(s->ext.ech_returned); + s->ext.ech_returned=NULL; + srval=OPENSSL_malloc(rlen); + if (!srval) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + memcpy(srval,rval,rlen); + s->ext.ech_returned=srval; + s->ext.ech_returned_len=rlen; + return 1; +} + +/** + * @brief Add ech_is_inner but only to inner CH for draft-10 + */ +EXT_RETURN tls_construct_ctos_ech_is_inner(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, size_t chainidx) +{ + if (s->ext.ech_grease==ECH_IS_GREASE || + (s->options & SSL_OP_ECH_GREASE) ) { + return EXT_RETURN_NOT_SENT; + } + if (!s->ech) { + return EXT_RETURN_NOT_SENT; + } + if (!s->ech->cfg) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (!s->ech->cfg->recs) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (s->ech->cfg->recs[0].version!=ECH_DRAFT_10_VERSION) { + return EXT_RETURN_NOT_SENT; + } + if (s->ext.ch_depth==1) { + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech_is_inner) + || !WPACKET_put_bytes_u16(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + return EXT_RETURN_SENT; + } + return EXT_RETURN_NOT_SENT; +} + +#endif /* END_OPENSSL_NO_ECH */ + diff --git a/tools/deps/custom_openvpn/patches/0001-Use-Point-to-Point-links-only-on-Windows-Vista-and-e.patch b/tools/deps/custom_openvpn/patches/0001-Use-Point-to-Point-links-only-on-Windows-Vista-and-e.patch new file mode 100644 index 000000000..37d8f7160 --- /dev/null +++ b/tools/deps/custom_openvpn/patches/0001-Use-Point-to-Point-links-only-on-Windows-Vista-and-e.patch @@ -0,0 +1,25 @@ +From 9fbabfd53559338736a32a206e5a1da6f2265340 Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jun 2023 23:34:35 +0300 +Subject: [PATCH 1/4] Use Point-to-Point links only on Windows Vista and + earlier + +--- + src/openvpn/options.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/openvpn/options.c b/src/openvpn/options.c +index ce7bd0a..32b820f 100644 +--- a/src/openvpn/options.c ++++ b/src/openvpn/options.c +@@ -3408,7 +3408,7 @@ options_postprocess_mutate_invariant(struct options *options) + /* delay may only be necessary when we perform DHCP handshake */ + const bool dhcp = (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ) + || (options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE); +- if ((options->mode == MODE_POINT_TO_POINT) && dhcp) ++ if ((options->mode == MODE_POINT_TO_POINT) && dhcp && (win32_version_info() <= WIN_VISTA)) + { + options->route_delay_defined = true; + options->route_delay = 5; /* Vista sometimes has a race without this */ +-- +2.40.1 + diff --git a/tools/deps/custom_openvpn/patches/0002-TUN-interface-name-windtun420.patch b/tools/deps/custom_openvpn/patches/0002-TUN-interface-name-windtun420.patch new file mode 100644 index 000000000..dbc58634a --- /dev/null +++ b/tools/deps/custom_openvpn/patches/0002-TUN-interface-name-windtun420.patch @@ -0,0 +1,24 @@ +From 7e3d668a260ed1b92b131a1fd68a4179695097ae Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jun 2023 23:39:36 +0300 +Subject: [PATCH 2/4] TUN interface name = windtun420 + +--- + src/openvpn/tun.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h +index e19e1a2..aa1d958 100644 +--- a/src/openvpn/tun.h ++++ b/src/openvpn/tun.h +@@ -43,7 +43,7 @@ + #include "dco.h" + + #ifdef _WIN32 +-#define WINTUN_COMPONENT_ID "wintun" ++#define WINTUN_COMPONENT_ID "windtun420" + #define DCO_WIN_REFERENCE_STRING "ovpn-dco" + + enum windows_driver_type { +-- +2.40.1 + diff --git a/tools/deps/custom_openvpn/patches/0003-OpenVPN-TCP-Split-reset-tcp-split-reset.patch b/tools/deps/custom_openvpn/patches/0003-OpenVPN-TCP-Split-reset-tcp-split-reset.patch new file mode 100644 index 000000000..07649bf8a --- /dev/null +++ b/tools/deps/custom_openvpn/patches/0003-OpenVPN-TCP-Split-reset-tcp-split-reset.patch @@ -0,0 +1,96 @@ +From 8ffc9853f6dfa5dcf08473f08d5d22fb932d4d28 Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jun 2023 23:30:37 +0300 +Subject: [PATCH 3/4] OpenVPN TCP Split-reset (--tcp-split-reset) + +Split a RESET packet into two pieces: 1-byte and remaining bytes. +Allows to bypass non-reassembling censorship filters with "unidentifiable" +data. + +Client-only option. +Should be enabled with "tcp-split-reset" in the configuration file +or --tcp-split-reset command line option. +--- + src/openvpn/options.c | 5 +++++ + src/openvpn/socket.c | 37 +++++++++++++++++++++++++++++++++++++ + src/openvpn/socket.h | 1 + + 3 files changed, 43 insertions(+) + +diff --git a/src/openvpn/options.c b/src/openvpn/options.c +index 32b820f..9bea414 100644 +--- a/src/openvpn/options.c ++++ b/src/openvpn/options.c +@@ -7809,6 +7809,11 @@ add_option(struct options *options, + VERIFY_PERMISSION(OPT_P_GENERAL); + options->server_flags |= SF_TCP_NODELAY_HELPER; + } ++ else if (streq(p[0], "tcp-split-reset") && !p[1]) ++ { ++ VERIFY_PERMISSION(OPT_P_GENERAL); ++ options->sockflags |= SF_TCP_SPLITRESET; ++ } + else if (streq(p[0], "stale-routes-check") && p[1] && !p[3]) + { + int ageing_time, check_interval; +diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c +index eff21ca..6f14559 100644 +--- a/src/openvpn/socket.c ++++ b/src/openvpn/socket.c +@@ -3409,7 +3409,44 @@ link_socket_write_tcp(struct link_socket *sock, + dmsg(D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset); + ASSERT(len <= sock->stream_buf.maxlen); + len = htonps(len); ++ ++ uint8_t opcode = *BPTR(buf) >> P_OPCODE_SHIFT; ++ + ASSERT(buf_write_prepend(buf, &len, sizeof(len))); ++ ++ if (sock->sockflags & SF_TCP_SPLITRESET) ++ { ++ int saved_len; ++ int size; ++ if (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ++ || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) ++ { ++ saved_len = buf->len; ++ buf->len = 1; ++ ++ socket_set_tcp_nodelay(sock->sd, 1); ++#ifdef _WIN32 ++ size = link_socket_write_win32(sock, buf, to); ++#else ++ size = link_socket_write_tcp_posix(sock, buf, to); ++#endif ++ if (!(sock->sockflags & SF_TCP_NODELAY)) ++ { ++ socket_set_tcp_nodelay(sock->sd, 0); ++ } ++ buf->len = saved_len; ++ buf_advance(buf, 1); ++ ++#ifdef _WIN32 ++ size += link_socket_write_win32(sock, buf, to); ++#else ++ size += link_socket_write_tcp_posix(sock, buf, to); ++#endif ++ return size; ++ } ++ } ++ + #ifdef _WIN32 + return link_socket_write_win32(sock, buf, to); + #else +diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h +index 605b6ad..afd1867 100644 +--- a/src/openvpn/socket.h ++++ b/src/openvpn/socket.h +@@ -207,6 +207,7 @@ struct link_socket + #define SF_PORT_SHARE (1<<2) + #define SF_HOST_RANDOMIZE (1<<3) + #define SF_GETADDRINFO_DGRAM (1<<4) ++#define SF_TCP_SPLITRESET (1<<5) + unsigned int sockflags; + int mark; + const char *bind_dev; +-- +2.40.1 + diff --git a/tools/deps/custom_openvpn/patches/0004-OpenVPN-UDP-Stuffing-udp-stuffing.patch b/tools/deps/custom_openvpn/patches/0004-OpenVPN-UDP-Stuffing-udp-stuffing.patch new file mode 100644 index 000000000..5dc8ce7fc --- /dev/null +++ b/tools/deps/custom_openvpn/patches/0004-OpenVPN-UDP-Stuffing-udp-stuffing.patch @@ -0,0 +1,93 @@ +From f239425150fb0fea662054459ec90b884bc99751 Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jun 2023 23:30:37 +0300 +Subject: [PATCH 4/4] OpenVPN UDP Stuffing (--udp-stuffing) + +Send a single UDP packet of random size with random payload before +the RESET packet, to feed the censorship filters with unidentifiable +data. + +Client-only option. +Should be enabled with "udp-stuffing" in the configuration file +or --udp-stuffing command line option. +--- + src/openvpn/options.c | 5 +++++ + src/openvpn/socket.h | 41 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/src/openvpn/options.c b/src/openvpn/options.c +index 9bea414..cd4656d 100644 +--- a/src/openvpn/options.c ++++ b/src/openvpn/options.c +@@ -7814,6 +7814,11 @@ add_option(struct options *options, + VERIFY_PERMISSION(OPT_P_GENERAL); + options->sockflags |= SF_TCP_SPLITRESET; + } ++ else if (streq(p[0], "udp-stuffing") && !p[1]) ++ { ++ VERIFY_PERMISSION(OPT_P_GENERAL); ++ options->sockflags |= SF_UDP_STUFFING; ++ } + else if (streq(p[0], "stale-routes-check") && p[1] && !p[3]) + { + int ageing_time, check_interval; +diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h +index afd1867..3cb54b2 100644 +--- a/src/openvpn/socket.h ++++ b/src/openvpn/socket.h +@@ -208,6 +208,7 @@ struct link_socket + #define SF_HOST_RANDOMIZE (1<<3) + #define SF_GETADDRINFO_DGRAM (1<<4) + #define SF_TCP_SPLITRESET (1<<5) ++#define SF_UDP_STUFFING (1<<6) + unsigned int sockflags; + int mark; + const char *bind_dev; +@@ -1167,6 +1168,46 @@ link_socket_write_udp(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) + { ++#ifndef P_CONTROL_HARD_RESET_CLIENT_V2 ++#define P_CONTROL_HARD_RESET_CLIENT_V2 7 ++#endif ++#ifndef P_CONTROL_HARD_RESET_CLIENT_V3 ++#define P_CONTROL_HARD_RESET_CLIENT_V3 10 ++#endif ++#ifndef P_OPCODE_SHIFT ++#define P_OPCODE_SHIFT 3 ++#endif ++ int rand_bytes(uint8_t *output, int len); ++ ++#define STUFFING_LEN_MAX 1200 ++ ++ uint8_t opcode = *BPTR(buf) >> P_OPCODE_SHIFT; ++ if ( ++ sock->sockflags & SF_UDP_STUFFING ++ && (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ++ || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) ++ ) ++ { ++ uint16_t stuffing_len; ++ rand_bytes((uint8_t*)&stuffing_len, sizeof(stuffing_len)); ++ stuffing_len = (stuffing_len % (STUFFING_LEN_MAX - 10)) + 10; ++ ++ uint8_t stuffing_data[STUFFING_LEN_MAX] = {0}; ++ rand_bytes(stuffing_data, sizeof(stuffing_data)); ++ struct buffer stuffing_buf = alloc_buf(STUFFING_LEN_MAX); ++ buf_write(&stuffing_buf, stuffing_data, stuffing_len); ++ ++#ifdef _WIN32 ++ link_socket_write_win32(sock, &stuffing_buf, to); ++#else ++ link_socket_write_udp_posix(sock, &stuffing_buf, to); ++#endif ++ ++ free_buf(&stuffing_buf); ++ } ++ ++ + #ifdef _WIN32 + return link_socket_write_win32(sock, buf, to); + #else +-- +2.40.1 + diff --git a/tools/deps/custom_openvpn/src/openvpn/options.c b/tools/deps/custom_openvpn/src/openvpn/options.c index 32b820f92..cd4656d74 100644 --- a/tools/deps/custom_openvpn/src/openvpn/options.c +++ b/tools/deps/custom_openvpn/src/openvpn/options.c @@ -7809,6 +7809,16 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->server_flags |= SF_TCP_NODELAY_HELPER; } + else if (streq(p[0], "tcp-split-reset") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->sockflags |= SF_TCP_SPLITRESET; + } + else if (streq(p[0], "udp-stuffing") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->sockflags |= SF_UDP_STUFFING; + } else if (streq(p[0], "stale-routes-check") && p[1] && !p[3]) { int ageing_time, check_interval; diff --git a/tools/deps/custom_openvpn/src/openvpn/socket.c b/tools/deps/custom_openvpn/src/openvpn/socket.c new file mode 100644 index 000000000..6ab19c8a8 --- /dev/null +++ b/tools/deps/custom_openvpn/src/openvpn/socket.c @@ -0,0 +1,4105 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2023 OpenVPN Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "socket.h" +#include "fdmisc.h" +#include "misc.h" +#include "gremlin.h" +#include "plugin.h" +#include "ps.h" +#include "run_command.h" +#include "manage.h" +#include "misc.h" +#include "manage.h" +#include "openvpn.h" +#include "forward.h" + +#include "memdbg.h" + +/* + * Convert sockflags/getaddr_flags into getaddr_flags + */ +static unsigned int +sf2gaf(const unsigned int getaddr_flags, + const unsigned int sockflags) +{ + if (sockflags & SF_HOST_RANDOMIZE) + { + return getaddr_flags | GETADDR_RANDOMIZE; + } + else + { + return getaddr_flags; + } +} + +/* + * Functions related to the translation of DNS names to IP addresses. + */ +static int +get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname, + void *network, unsigned int *netbits, + int resolve_retry_seconds, struct signal_info *sig_info, + int msglevel) +{ + char *endp, *sep, *var_host = NULL; + struct addrinfo *ai = NULL; + unsigned long bits; + uint8_t max_bits; + int ret = -1; + + if (!hostname) + { + msg(M_NONFATAL, "Can't resolve null hostname!"); + goto out; + } + + /* assign family specific default values */ + switch (af) + { + case AF_INET: + bits = 0; + max_bits = sizeof(in_addr_t) * 8; + break; + + case AF_INET6: + bits = 64; + max_bits = sizeof(struct in6_addr) * 8; + break; + + default: + msg(M_WARN, + "Unsupported AF family passed to getaddrinfo for %s (%d)", + hostname, af); + goto out; + } + + /* we need to modify the hostname received as input, but we don't want to + * touch it directly as it might be a constant string. + * + * Therefore, we clone the string here and free it at the end of the + * function */ + var_host = strdup(hostname); + if (!var_host) + { + msg(M_NONFATAL | M_ERRNO, + "Can't allocate hostname buffer for getaddrinfo"); + goto out; + } + + /* check if this hostname has a /bits suffix */ + sep = strchr(var_host, '/'); + if (sep) + { + bits = strtoul(sep + 1, &endp, 10); + if ((*endp != '\0') || (bits > max_bits)) + { + msg(msglevel, "IP prefix '%s': invalid '/bits' spec (%s)", hostname, + sep + 1); + goto out; + } + *sep = '\0'; + } + + ret = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, var_host, NULL, + resolve_retry_seconds, sig_info, af, &ai); + if ((ret == 0) && network) + { + struct in6_addr *ip6; + in_addr_t *ip4; + + switch (af) + { + case AF_INET: + ip4 = network; + *ip4 = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr; + + if (flags & GETADDR_HOST_ORDER) + { + *ip4 = ntohl(*ip4); + } + break; + + case AF_INET6: + ip6 = network; + *ip6 = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + break; + + default: + /* can't get here because 'af' was previously checked */ + msg(M_WARN, + "Unsupported AF family for %s (%d)", var_host, af); + goto out; + } + } + + if (netbits) + { + *netbits = bits; + } + + /* restore '/' separator, if any */ + if (sep) + { + *sep = '/'; + } +out: + freeaddrinfo(ai); + free(var_host); + + return ret; +} + +in_addr_t +getaddr(unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + struct signal_info *sig_info) +{ + in_addr_t addr; + int status; + + status = get_addr_generic(AF_INET, flags, hostname, &addr, NULL, + resolve_retry_seconds, sig_info, + M_WARN); + if (status==0) + { + if (succeeded) + { + *succeeded = true; + } + return addr; + } + else + { + if (succeeded) + { + *succeeded = false; + } + return 0; + } +} + +bool +get_ipv6_addr(const char *hostname, struct in6_addr *network, + unsigned int *netbits, int msglevel) +{ + if (get_addr_generic(AF_INET6, GETADDR_RESOLVE, hostname, network, netbits, + 0, NULL, msglevel) < 0) + { + return false; + } + + return true; /* parsing OK, values set */ +} + +static inline bool +streqnull(const char *a, const char *b) +{ + if (a == NULL && b == NULL) + { + return true; + } + else if (a == NULL || b == NULL) + { + return false; + } + else + { + return streq(a, b); + } +} + +/* + * get_cached_dns_entry return 0 on success and -1 + * otherwise. (like getaddrinfo) + */ +static int +get_cached_dns_entry(struct cached_dns_entry *dns_cache, + const char *hostname, + const char *servname, + int ai_family, + int resolve_flags, + struct addrinfo **ai) +{ + struct cached_dns_entry *ph; + int flags; + + /* Only use flags that are relevant for the structure */ + flags = resolve_flags & GETADDR_CACHE_MASK; + + for (ph = dns_cache; ph; ph = ph->next) + { + if (streqnull(ph->hostname, hostname) + && streqnull(ph->servname, servname) + && ph->ai_family == ai_family + && ph->flags == flags) + { + *ai = ph->ai; + return 0; + } + } + return -1; +} + + +static int +do_preresolve_host(struct context *c, + const char *hostname, + const char *servname, + const int af, + const int flags) +{ + struct addrinfo *ai; + int status; + + if (get_cached_dns_entry(c->c1.dns_cache, + hostname, + servname, + af, + flags, + &ai) == 0) + { + /* entry already cached, return success */ + return 0; + } + + status = openvpn_getaddrinfo(flags, hostname, servname, + c->options.resolve_retry_seconds, NULL, + af, &ai); + if (status == 0) + { + struct cached_dns_entry *ph; + + ALLOC_OBJ_CLEAR_GC(ph, struct cached_dns_entry, &c->gc); + ph->ai = ai; + ph->hostname = hostname; + ph->servname = servname; + ph->flags = flags & GETADDR_CACHE_MASK; + + if (!c->c1.dns_cache) + { + c->c1.dns_cache = ph; + } + else + { + struct cached_dns_entry *prev = c->c1.dns_cache; + while (prev->next) + { + prev = prev->next; + } + prev->next = ph; + } + + gc_addspecial(ai, &gc_freeaddrinfo_callback, &c->gc); + + } + return status; +} + +void +do_preresolve(struct context *c) +{ + int i; + struct connection_list *l = c->options.connection_list; + const unsigned int preresolve_flags = GETADDR_RESOLVE + |GETADDR_UPDATE_MANAGEMENT_STATE + |GETADDR_MENTION_RESOLVE_RETRY + |GETADDR_FATAL; + + + for (i = 0; i < l->len; ++i) + { + int status; + const char *remote; + int flags = preresolve_flags; + + struct connection_entry *ce = c->options.connection_list->array[i]; + + if (proto_is_dgram(ce->proto)) + { + flags |= GETADDR_DATAGRAM; + } + + if (c->options.sockflags & SF_HOST_RANDOMIZE) + { + flags |= GETADDR_RANDOMIZE; + } + + if (c->options.ip_remote_hint) + { + remote = c->options.ip_remote_hint; + } + else + { + remote = ce->remote; + } + + /* HTTP remote hostname does not need to be resolved */ + if (!ce->http_proxy_options) + { + status = do_preresolve_host(c, remote, ce->remote_port, + ce->af, flags); + if (status != 0) + { + goto err; + } + } + + /* Preresolve proxy */ + if (ce->http_proxy_options) + { + status = do_preresolve_host(c, + ce->http_proxy_options->server, + ce->http_proxy_options->port, + ce->af, + preresolve_flags); + + if (status != 0) + { + goto err; + } + } + + if (ce->socks_proxy_server) + { + status = do_preresolve_host(c, + ce->socks_proxy_server, + ce->socks_proxy_port, + ce->af, + flags); + if (status != 0) + { + goto err; + } + } + + if (ce->bind_local) + { + flags |= GETADDR_PASSIVE; + flags &= ~GETADDR_RANDOMIZE; + status = do_preresolve_host(c, ce->local, ce->local_port, + ce->af, flags); + if (status != 0) + { + goto err; + } + + } + + } + return; + +err: + throw_signal_soft(SIGHUP, "Preresolving failed"); +} + +/* + * Translate IPv4/IPv6 addr or hostname into struct addrinfo + * If resolve error, try again for resolve_retry_seconds seconds. + */ +int +openvpn_getaddrinfo(unsigned int flags, + const char *hostname, + const char *servname, + int resolve_retry_seconds, + struct signal_info *sig_info, + int ai_family, + struct addrinfo **res) +{ + struct addrinfo hints; + int status; + struct signal_info sigrec = {0}; + int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; + struct gc_arena gc = gc_new(); + const char *print_hostname; + const char *print_servname; + + ASSERT(res); + + ASSERT(hostname || servname); + ASSERT(!(flags & GETADDR_HOST_ORDER)); + + if (servname) + { + print_servname = servname; + } + else + { + print_servname = ""; + } + + if (flags & GETADDR_MSG_VIRT_OUT) + { + msglevel |= M_MSG_VIRT_OUT; + } + + if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL)) + && !sig_info) + { + sig_info = &sigrec; + } + + /* try numeric ipv6 addr first */ + CLEAR(hints); + hints.ai_family = ai_family; + hints.ai_flags = AI_NUMERICHOST; + + if (flags & GETADDR_PASSIVE) + { + hints.ai_flags |= AI_PASSIVE; + } + + if (flags & GETADDR_DATAGRAM) + { + hints.ai_socktype = SOCK_DGRAM; + } + else + { + hints.ai_socktype = SOCK_STREAM; + } + + status = getaddrinfo(hostname, servname, &hints, res); + + if (status != 0) /* parse as numeric address failed? */ + { + const int fail_wait_interval = 5; /* seconds */ + /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 */ + int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : + ((resolve_retry_seconds + 4)/ fail_wait_interval); + const char *fmt; + int level = 0; + + if (hostname && (flags & GETADDR_RANDOMIZE)) + { + hostname = hostname_randomize(hostname, &gc); + } + + if (hostname) + { + print_hostname = hostname; + } + else + { + print_hostname = "undefined"; + } + + fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s)"; + if ((flags & GETADDR_MENTION_RESOLVE_RETRY) + && !resolve_retry_seconds) + { + fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s) " + "(I would have retried this name query if you had " + "specified the --resolv-retry option.)"; + } + + if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL) + { + msg(msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)", + print_hostname, print_servname, gai_strerror(status)); + goto done; + } + +#ifdef ENABLE_MANAGEMENT + if (flags & GETADDR_UPDATE_MANAGEMENT_STATE) + { + if (management) + { + management_set_state(management, + OPENVPN_STATE_RESOLVE, + NULL, + NULL, + NULL, + NULL, + NULL); + } + } +#endif + + /* + * Resolve hostname + */ + while (true) + { +#ifndef _WIN32 + /* force resolv.conf reload */ + res_init(); +#endif + /* try hostname lookup */ + hints.ai_flags &= ~AI_NUMERICHOST; + dmsg(D_SOCKET_DEBUG, + "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d", + flags, hints.ai_family, hints.ai_socktype); + status = getaddrinfo(hostname, servname, &hints, res); + + if (sig_info) + { + get_signal(&sig_info->signal_received); + if (sig_info->signal_received) /* were we interrupted by a signal? */ + { + /* why are we overwriting SIGUSR1 ? */ + if (sig_info->signal_received == SIGUSR1) /* ignore SIGUSR1 */ + { + msg(level, + "RESOLVE: Ignored SIGUSR1 signal received during " + "DNS resolution attempt"); + signal_reset(sig_info); + } + else + { + /* turn success into failure (interrupted syscall) */ + if (0 == status) + { + ASSERT(res); + freeaddrinfo(*res); + *res = NULL; + status = EAI_AGAIN; /* = temporary failure */ + errno = EINTR; + } + goto done; + } + } + } + + /* success? */ + if (0 == status) + { + break; + } + + /* resolve lookup failed, should we + * continue or fail? */ + level = msglevel; + if (resolve_retries > 0) + { + level = D_RESOLVE_ERRORS; + } + + msg(level, + fmt, + print_hostname, + print_servname, + gai_strerror(status)); + + if (--resolve_retries <= 0) + { + goto done; + } + + management_sleep(fail_wait_interval); + } + + ASSERT(res); + + /* hostname resolve succeeded */ + + /* + * Do not choose an IP Addresse by random or change the order * + * of IP addresses, doing so will break RFC 3484 address selection * + */ + } + else + { + /* IP address parse succeeded */ + if (flags & GETADDR_RANDOMIZE) + { + msg(M_WARN, + "WARNING: ignoring --remote-random-hostname because the " + "hostname is an IP address"); + } + } + +done: + if (sig_info && sig_info->signal_received) + { + int level = 0; + if (flags & GETADDR_FATAL_ON_SIGNAL) + { + level = M_FATAL; + } + else if (flags & GETADDR_WARN_ON_SIGNAL) + { + level = M_WARN; + } + msg(level, "RESOLVE: signal received during DNS resolution attempt"); + } + + gc_free(&gc); + return status; +} + +/* + * We do our own inet_aton because the glibc function + * isn't very good about error checking. + */ +int +openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr) +{ + unsigned int a, b, c, d; + + CLEAR(*addr); + if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) + { + if (a < 256 && b < 256 && c < 256 && d < 256) + { + addr->s_addr = htonl(a<<24 | b<<16 | c<<8 | d); + return OIA_IP; /* good dotted quad */ + } + } + if (string_class(dotted_quad, CC_DIGIT|CC_DOT, 0)) + { + return OIA_ERROR; /* probably a badly formatted dotted quad */ + } + else + { + return OIA_HOSTNAME; /* probably a hostname */ + } +} + +bool +ip_addr_dotted_quad_safe(const char *dotted_quad) +{ + /* verify non-NULL */ + if (!dotted_quad) + { + return false; + } + + /* verify length is within limits */ + if (strlen(dotted_quad) > 15) + { + return false; + } + + /* verify that all chars are either numeric or '.' and that no numeric + * substring is greater than 3 chars */ + { + int nnum = 0; + const char *p = dotted_quad; + int c; + + while ((c = *p++)) + { + if (c >= '0' && c <= '9') + { + ++nnum; + if (nnum > 3) + { + return false; + } + } + else if (c == '.') + { + nnum = 0; + } + else + { + return false; + } + } + } + + /* verify that string will convert to IP address */ + { + struct in_addr a; + return openvpn_inet_aton(dotted_quad, &a) == OIA_IP; + } +} + +bool +ipv6_addr_safe(const char *ipv6_text_addr) +{ + /* verify non-NULL */ + if (!ipv6_text_addr) + { + return false; + } + + /* verify length is within limits */ + if (strlen(ipv6_text_addr) > INET6_ADDRSTRLEN) + { + return false; + } + + /* verify that string will convert to IPv6 address */ + { + struct in6_addr a6; + return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1; + } +} + +static bool +dns_addr_safe(const char *addr) +{ + if (addr) + { + const size_t len = strlen(addr); + return len > 0 && len <= 255 && string_class(addr, CC_ALNUM|CC_DASH|CC_DOT, 0); + } + else + { + return false; + } +} + +bool +ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn) +{ + if (ip_addr_dotted_quad_safe(addr)) + { + return true; + } + else if (allow_fqdn) + { + return dns_addr_safe(addr); + } + else + { + return false; + } +} + +bool +mac_addr_safe(const char *mac_addr) +{ + /* verify non-NULL */ + if (!mac_addr) + { + return false; + } + + /* verify length is within limits */ + if (strlen(mac_addr) > 17) + { + return false; + } + + /* verify that all chars are either alphanumeric or ':' and that no + * alphanumeric substring is greater than 2 chars */ + { + int nnum = 0; + const char *p = mac_addr; + int c; + + while ((c = *p++)) + { + if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + { + ++nnum; + if (nnum > 2) + { + return false; + } + } + else if (c == ':') + { + nnum = 0; + } + else + { + return false; + } + } + } + + /* error-checking is left to script invoked in lladdr.c */ + return true; +} + +static int +socket_get_sndbuf(socket_descriptor_t sd) +{ +#if defined(SOL_SOCKET) && defined(SO_SNDBUF) + int val; + socklen_t len; + + len = sizeof(val); + if (getsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &val, &len) == 0 + && len == sizeof(val)) + { + return val; + } +#endif + return 0; +} + +static void +socket_set_sndbuf(socket_descriptor_t sd, int size) +{ +#if defined(SOL_SOCKET) && defined(SO_SNDBUF) + if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) != 0) + { + msg(M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size); + } +#endif +} + +static int +socket_get_rcvbuf(socket_descriptor_t sd) +{ +#if defined(SOL_SOCKET) && defined(SO_RCVBUF) + int val; + socklen_t len; + + len = sizeof(val); + if (getsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *) &val, &len) == 0 + && len == sizeof(val)) + { + return val; + } +#endif + return 0; +} + +static bool +socket_set_rcvbuf(socket_descriptor_t sd, int size) +{ +#if defined(SOL_SOCKET) && defined(SO_RCVBUF) + if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof(size)) != 0) + { + msg(M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size); + return false; + } + return true; +#endif +} + +static void +socket_set_buffers(socket_descriptor_t fd, const struct socket_buffer_size *sbs) +{ + if (sbs) + { + const int sndbuf_old = socket_get_sndbuf(fd); + const int rcvbuf_old = socket_get_rcvbuf(fd); + + if (sbs->sndbuf) + { + socket_set_sndbuf(fd, sbs->sndbuf); + } + + if (sbs->rcvbuf) + { + socket_set_rcvbuf(fd, sbs->rcvbuf); + } + + msg(D_OSBUF, "Socket Buffers: R=[%d->%d] S=[%d->%d]", + rcvbuf_old, + socket_get_rcvbuf(fd), + sndbuf_old, + socket_get_sndbuf(fd)); + } +} + +/* + * Set other socket options + */ + +static bool +socket_set_tcp_nodelay(socket_descriptor_t sd, int state) +{ +#if defined(_WIN32) || (defined(IPPROTO_TCP) && defined(TCP_NODELAY)) + if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof(state)) != 0) + { + msg(M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state); + return false; + } + else + { + dmsg(D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state); + return true; + } +#else /* if defined(_WIN32) || (defined(IPPROTO_TCP) && defined(TCP_NODELAY)) */ + msg(M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed (No kernel support)", state); + return false; +#endif +} + +static inline void +socket_set_mark(socket_descriptor_t sd, int mark) +{ +#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK + if (mark && setsockopt(sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof(mark)) != 0) + { + msg(M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark); + } +#endif +} + +static bool +socket_set_flags(socket_descriptor_t sd, unsigned int sockflags) +{ + if (sockflags & SF_TCP_NODELAY) + { + return socket_set_tcp_nodelay(sd, 1); + } + else + { + return true; + } +} + +bool +link_socket_update_flags(struct link_socket *ls, unsigned int sockflags) +{ + if (ls && socket_defined(ls->sd)) + { + return socket_set_flags(ls->sd, ls->sockflags = sockflags); + } + else + { + return false; + } +} + +void +link_socket_update_buffer_sizes(struct link_socket *ls, int rcvbuf, int sndbuf) +{ + if (ls && socket_defined(ls->sd)) + { + ls->socket_buffer_sizes.sndbuf = sndbuf; + ls->socket_buffer_sizes.rcvbuf = rcvbuf; + socket_set_buffers(ls->sd, &ls->socket_buffer_sizes); + } +} + +/* + * SOCKET INITIALIZATION CODE. + * Create a TCP/UDP socket + */ + +socket_descriptor_t +create_socket_tcp(struct addrinfo *addrinfo) +{ + socket_descriptor_t sd; + + ASSERT(addrinfo); + ASSERT(addrinfo->ai_socktype == SOCK_STREAM); + + if ((sd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + { + msg(M_ERR, "Cannot create TCP socket"); + } + +#ifndef _WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */ + /* set SO_REUSEADDR on socket */ + { + int on = 1; + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof(on)) < 0) + { + msg(M_ERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP socket"); + } + } +#endif + + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(sd); + + return sd; +} + +static socket_descriptor_t +create_socket_udp(struct addrinfo *addrinfo, const unsigned int flags) +{ + socket_descriptor_t sd; + + ASSERT(addrinfo); + ASSERT(addrinfo->ai_socktype == SOCK_DGRAM); + + if ((sd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + { + msg(M_ERR, "UDP: Cannot create UDP/UDP6 socket"); + } +#if ENABLE_IP_PKTINFO + else if (flags & SF_USE_IP_PKTINFO) + { + int pad = 1; + if (addrinfo->ai_family == AF_INET) + { +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + if (setsockopt(sd, SOL_IP, IP_PKTINFO, + (void *)&pad, sizeof(pad)) < 0) + { + msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO"); + } +#elif defined(IP_RECVDSTADDR) + if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, + (void *)&pad, sizeof(pad)) < 0) + { + msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR"); + } +#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + } + else if (addrinfo->ai_family == AF_INET6) + { +#ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_PKTINFO, + (void *)&pad, sizeof(pad)) < 0) +#else + if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void *)&pad, sizeof(pad)) < 0) +#endif + { msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");} + } + } +#endif /* if ENABLE_IP_PKTINFO */ + + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(sd); + + return sd; +} + +static void +bind_local(struct link_socket *sock, const sa_family_t ai_family) +{ + /* bind to local address/port */ + if (sock->bind_local) + { + if (sock->socks_proxy && sock->info.proto == PROTO_UDP) + { + socket_bind(sock->ctrl_sd, sock->info.lsa->bind_local, + ai_family, "SOCKS", false); + } + else + { + socket_bind(sock->sd, sock->info.lsa->bind_local, + ai_family, + "TCP/UDP", sock->info.bind_ipv6_only); + } + } +} + +static void +create_socket(struct link_socket *sock, struct addrinfo *addr) +{ + if (addr->ai_protocol == IPPROTO_UDP || addr->ai_socktype == SOCK_DGRAM) + { + sock->sd = create_socket_udp(addr, sock->sockflags); + sock->sockflags |= SF_GETADDRINFO_DGRAM; + + /* Assume that control socket and data socket to the socks proxy + * are using the same IP family */ + if (sock->socks_proxy) + { + /* Construct a temporary addrinfo to create the socket, + * currently resolve two remote addresses is not supported, + * TODO: Rewrite the whole resolve_remote */ + struct addrinfo addrinfo_tmp = *addr; + addrinfo_tmp.ai_socktype = SOCK_STREAM; + addrinfo_tmp.ai_protocol = IPPROTO_TCP; + sock->ctrl_sd = create_socket_tcp(&addrinfo_tmp); + } + } + else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM) + { + sock->sd = create_socket_tcp(addr); + } + else + { + ASSERT(0); + } + /* Set af field of sock->info, so it always reflects the address family + * of the created socket */ + sock->info.af = addr->ai_family; + + /* set socket buffers based on --sndbuf and --rcvbuf options */ + socket_set_buffers(sock->sd, &sock->socket_buffer_sizes); + + /* set socket to --mark packets with given value */ + socket_set_mark(sock->sd, sock->mark); + +#if defined(TARGET_LINUX) + if (sock->bind_dev) + { + msg(M_INFO, "Using bind-dev %s", sock->bind_dev); + if (setsockopt(sock->sd, SOL_SOCKET, SO_BINDTODEVICE, sock->bind_dev, strlen(sock->bind_dev) + 1) != 0) + { + msg(M_WARN|M_ERRNO, "WARN: setsockopt SO_BINDTODEVICE=%s failed", sock->bind_dev); + } + + } +#endif + + bind_local(sock, addr->ai_family); +} + +#ifdef TARGET_ANDROID +static void +protect_fd_nonlocal(int fd, const struct sockaddr *addr) +{ + if (!management) + { + msg(M_FATAL, "Required management interface not available.") + } + + /* pass socket FD to management interface to pass on to VPNService API + * as "protected socket" (exempt from being routed into tunnel) + */ + if (addr_local(addr)) + { + msg(D_SOCKET_DEBUG, "Address is local, not protecting socket fd %d", fd); + return; + } + + msg(D_SOCKET_DEBUG, "Protecting socket fd %d", fd); + management->connection.fdtosend = fd; + management_android_control(management, "PROTECTFD", __func__); +} +#endif + +/* + * Functions used for establishing a TCP stream connection. + */ +static void +socket_do_listen(socket_descriptor_t sd, + const struct addrinfo *local, + bool do_listen, + bool do_set_nonblock) +{ + struct gc_arena gc = gc_new(); + if (do_listen) + { + ASSERT(local); + msg(M_INFO, "Listening for incoming TCP connection on %s", + print_sockaddr(local->ai_addr, &gc)); + if (listen(sd, 32)) + { + msg(M_ERR, "TCP: listen() failed"); + } + } + + /* set socket to non-blocking mode */ + if (do_set_nonblock) + { + set_nonblock(sd); + } + + gc_free(&gc); +} + +socket_descriptor_t +socket_do_accept(socket_descriptor_t sd, + struct link_socket_actual *act, + const bool nowait) +{ + /* af_addr_size WILL return 0 in this case if AFs other than AF_INET + * are compiled because act is empty here. + * could use getsockname() to support later remote_len check + */ + socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family); + socklen_t remote_len = sizeof(act->dest.addr); + socket_descriptor_t new_sd = SOCKET_UNDEFINED; + + CLEAR(*act); + + if (nowait) + { + new_sd = getpeername(sd, &act->dest.addr.sa, &remote_len); + + if (!socket_defined(new_sd)) + { + msg(D_LINK_ERRORS | M_ERRNO, "TCP: getpeername() failed"); + } + else + { + new_sd = sd; + } + } + else + { + new_sd = accept(sd, &act->dest.addr.sa, &remote_len); + } + +#if 0 /* For debugging only, test the effect of accept() failures */ + { + static int foo = 0; + ++foo; + if (foo & 1) + { + new_sd = -1; + } + } +#endif + + if (!socket_defined(new_sd)) + { + msg(D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", (int)sd); + } + /* only valid if we have remote_len_af!=0 */ + else if (remote_len_af && remote_len != remote_len_af) + { + msg(D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len); + openvpn_close_socket(new_sd); + new_sd = SOCKET_UNDEFINED; + } + else + { + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(sd); + } + return new_sd; +} + +static void +tcp_connection_established(const struct link_socket_actual *act) +{ + struct gc_arena gc = gc_new(); + msg(M_INFO, "TCP connection established with %s", + print_link_socket_actual(act, &gc)); + gc_free(&gc); +} + +static socket_descriptor_t +socket_listen_accept(socket_descriptor_t sd, + struct link_socket_actual *act, + const char *remote_dynamic, + const struct addrinfo *local, + bool do_listen, + bool nowait, + volatile int *signal_received) +{ + struct gc_arena gc = gc_new(); + /* struct openvpn_sockaddr *remote = &act->dest; */ + struct openvpn_sockaddr remote_verify = act->dest; + socket_descriptor_t new_sd = SOCKET_UNDEFINED; + + CLEAR(*act); + socket_do_listen(sd, local, do_listen, true); + + while (true) + { + int status; + fd_set reads; + struct timeval tv; + + FD_ZERO(&reads); + openvpn_fd_set(sd, &reads); + tv.tv_sec = 0; + tv.tv_usec = 0; + + status = select(sd + 1, &reads, NULL, NULL, &tv); + + get_signal(signal_received); + if (*signal_received) + { + gc_free(&gc); + return sd; + } + + if (status < 0) + { + msg(D_LINK_ERRORS | M_ERRNO, "TCP: select() failed"); + } + + if (status <= 0) + { + management_sleep(1); + continue; + } + + new_sd = socket_do_accept(sd, act, nowait); + + if (socket_defined(new_sd)) + { + struct addrinfo *ai = NULL; + if (remote_dynamic) + { + openvpn_getaddrinfo(0, remote_dynamic, NULL, 1, NULL, + remote_verify.addr.sa.sa_family, &ai); + } + + if (ai && !addrlist_match(&remote_verify, ai)) + { + msg(M_WARN, + "TCP NOTE: Rejected connection attempt from %s due to --remote setting", + print_link_socket_actual(act, &gc)); + if (openvpn_close_socket(new_sd)) + { + msg(M_ERR, "TCP: close socket failed (new_sd)"); + } + freeaddrinfo(ai); + } + else + { + if (ai) + { + freeaddrinfo(ai); + } + break; + } + } + management_sleep(1); + } + + if (!nowait && openvpn_close_socket(sd)) + { + msg(M_ERR, "TCP: close socket failed (sd)"); + } + + tcp_connection_established(act); + + gc_free(&gc); + return new_sd; +} + +void +socket_bind(socket_descriptor_t sd, + struct addrinfo *local, + int ai_family, + const char *prefix, + bool ipv6only) +{ + struct gc_arena gc = gc_new(); + + /* FIXME (schwabe) + * getaddrinfo for the bind address might return multiple AF_INET/AF_INET6 + * entries for the requested protocol. + * For example if an address has multiple A records + * What is the correct way to deal with it? + */ + + struct addrinfo *cur; + + ASSERT(local); + + + /* find the first addrinfo with correct ai_family */ + for (cur = local; cur; cur = cur->ai_next) + { + if (cur->ai_family == ai_family) + { + break; + } + } + if (!cur) + { + msg(M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record", + prefix, addr_family_name(ai_family)); + } + + if (ai_family == AF_INET6) + { + int v6only = ipv6only ? 1 : 0; /* setsockopt must have an "int" */ + + msg(M_INFO, "setsockopt(IPV6_V6ONLY=%d)", v6only); + if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only))) + { + msg(M_NONFATAL|M_ERRNO, "Setting IPV6_V6ONLY=%d failed", v6only); + } + } + if (bind(sd, cur->ai_addr, cur->ai_addrlen)) + { + msg(M_FATAL | M_ERRNO, "%s: Socket bind failed on local address %s", + prefix, + print_sockaddr_ex(local->ai_addr, ":", PS_SHOW_PORT, &gc)); + } + gc_free(&gc); +} + +int +openvpn_connect(socket_descriptor_t sd, + const struct sockaddr *remote, + int connect_timeout, + volatile int *signal_received) +{ + int status = 0; + +#ifdef TARGET_ANDROID + protect_fd_nonlocal(sd, remote); +#endif + + set_nonblock(sd); + status = connect(sd, remote, af_addr_size(remote->sa_family)); + if (status) + { + status = openvpn_errno(); + } + if ( +#ifdef _WIN32 + status == WSAEWOULDBLOCK +#else + status == EINPROGRESS +#endif + ) + { + while (true) + { +#if POLL + struct pollfd fds[1]; + fds[0].fd = sd; + fds[0].events = POLLOUT; + status = poll(fds, 1, (connect_timeout > 0) ? 1000 : 0); +#else + fd_set writes; + struct timeval tv; + + FD_ZERO(&writes); + openvpn_fd_set(sd, &writes); + tv.tv_sec = (connect_timeout > 0) ? 1 : 0; + tv.tv_usec = 0; + + status = select(sd + 1, NULL, &writes, NULL, &tv); +#endif + if (signal_received) + { + get_signal(signal_received); + if (*signal_received) + { + status = 0; + break; + } + } + if (status < 0) + { + status = openvpn_errno(); + break; + } + if (status <= 0) + { + if (--connect_timeout < 0) + { +#ifdef _WIN32 + status = WSAETIMEDOUT; +#else + status = ETIMEDOUT; +#endif + break; + } + management_sleep(0); + continue; + } + + /* got it */ + { + int val = 0; + socklen_t len; + + len = sizeof(val); + if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (void *) &val, &len) == 0 + && len == sizeof(val)) + { + status = val; + } + else + { + status = openvpn_errno(); + } + break; + } + } + } + + return status; +} + +void +set_actual_address(struct link_socket_actual *actual, struct addrinfo *ai) +{ + CLEAR(*actual); + ASSERT(ai); + + if (ai->ai_family == AF_INET) + { + actual->dest.addr.in4 = + *((struct sockaddr_in *) ai->ai_addr); + } + else if (ai->ai_family == AF_INET6) + { + actual->dest.addr.in6 = + *((struct sockaddr_in6 *) ai->ai_addr); + } + else + { + ASSERT(0); + } + +} + +static void +socket_connect(socket_descriptor_t *sd, + const struct sockaddr *dest, + const int connect_timeout, + struct signal_info *sig_info) +{ + struct gc_arena gc = gc_new(); + int status; + + msg(M_INFO, "Attempting to establish TCP connection with %s", + print_sockaddr(dest, &gc)); + +#ifdef ENABLE_MANAGEMENT + if (management) + { + management_set_state(management, + OPENVPN_STATE_TCP_CONNECT, + NULL, + NULL, + NULL, + NULL, + NULL); + } +#endif + + /* Set the actual address */ + status = openvpn_connect(*sd, dest, connect_timeout, &sig_info->signal_received); + + get_signal(&sig_info->signal_received); + if (sig_info->signal_received) + { + goto done; + } + + if (status) + { + + msg(D_LINK_ERRORS, "TCP: connect to %s failed: %s", + print_sockaddr(dest, &gc), strerror(status)); + + openvpn_close_socket(*sd); + *sd = SOCKET_UNDEFINED; + register_signal(sig_info, SIGUSR1, "connection-failed"); + sig_info->source = SIG_SOURCE_CONNECTION_FAILED; + } + else + { + msg(M_INFO, "TCP connection established with %s", + print_sockaddr(dest, &gc)); + } + +done: + gc_free(&gc); +} + +/* + * Stream buffer handling prototypes -- stream_buf is a helper class + * to assist in the packetization of stream transport protocols + * such as TCP. + */ + +static void +stream_buf_init(struct stream_buf *sb, struct buffer *buf, + const unsigned int sockflags, const int proto); + +static void +stream_buf_close(struct stream_buf *sb); + +static bool +stream_buf_added(struct stream_buf *sb, int length_added); + +/* For stream protocols, allocate a buffer to build up packet. + * Called after frame has been finalized. */ + +static void +socket_frame_init(const struct frame *frame, struct link_socket *sock) +{ +#ifdef _WIN32 + overlapped_io_init(&sock->reads, frame, FALSE); + overlapped_io_init(&sock->writes, frame, TRUE); + sock->rw_handle.read = sock->reads.overlapped.hEvent; + sock->rw_handle.write = sock->writes.overlapped.hEvent; +#endif + + if (link_socket_connection_oriented(sock)) + { +#ifdef _WIN32 + stream_buf_init(&sock->stream_buf, + &sock->reads.buf_init, + sock->sockflags, + sock->info.proto); +#else + alloc_buf_sock_tun(&sock->stream_buf_data, frame); + + stream_buf_init(&sock->stream_buf, + &sock->stream_buf_data, + sock->sockflags, + sock->info.proto); +#endif + } +} + +static void +resolve_bind_local(struct link_socket *sock, const sa_family_t af) +{ + struct gc_arena gc = gc_new(); + + /* resolve local address if undefined */ + if (!sock->info.lsa->bind_local) + { + int flags = GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL + |GETADDR_FATAL | GETADDR_PASSIVE; + int status; + + if (proto_is_dgram(sock->info.proto)) + { + flags |= GETADDR_DATAGRAM; + } + + /* will return AF_{INET|INET6}from local_host */ + status = get_cached_dns_entry(sock->dns_cache, + sock->local_host, + sock->local_port, + af, + flags, + &sock->info.lsa->bind_local); + + if (status) + { + status = openvpn_getaddrinfo(flags, sock->local_host, sock->local_port, 0, + NULL, af, &sock->info.lsa->bind_local); + } + + if (status !=0) + { + msg(M_FATAL, "getaddrinfo() failed for local \"%s:%s\": %s", + sock->local_host, sock->local_port, + gai_strerror(status)); + } + } + + gc_free(&gc); +} + +static void +resolve_remote(struct link_socket *sock, + int phase, + const char **remote_dynamic, + struct signal_info *sig_info) +{ + volatile int *signal_received = sig_info ? &sig_info->signal_received : NULL; + struct gc_arena gc = gc_new(); + + /* resolve remote address if undefined */ + if (!sock->info.lsa->remote_list) + { + if (sock->remote_host) + { + unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags); + int retry = 0; + int status = -1; + struct addrinfo *ai; + if (proto_is_dgram(sock->info.proto)) + { + flags |= GETADDR_DATAGRAM; + } + + if (sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) + { + if (phase == 2) + { + flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); + } + retry = 0; + } + else if (phase == 1) + { + if (sock->resolve_retry_seconds) + { + retry = 0; + } + else + { + flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); + retry = 0; + } + } + else if (phase == 2) + { + if (sock->resolve_retry_seconds) + { + flags |= GETADDR_FATAL; + retry = sock->resolve_retry_seconds; + } + else + { + ASSERT(0); + } + } + else + { + ASSERT(0); + } + + + status = get_cached_dns_entry(sock->dns_cache, + sock->remote_host, + sock->remote_port, + sock->info.af, + flags, &ai); + if (status) + { + status = openvpn_getaddrinfo(flags, sock->remote_host, sock->remote_port, + retry, sig_info, sock->info.af, &ai); + } + + if (status == 0) + { + sock->info.lsa->remote_list = ai; + sock->info.lsa->current_remote = ai; + + dmsg(D_SOCKET_DEBUG, + "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", + flags, + phase, + retry, + signal_received ? *signal_received : -1, + status); + } + if (signal_received && *signal_received) + { + goto done; + } + if (status!=0) + { + if (signal_received) + { + /* potential overwrite of signal */ + register_signal(sig_info, SIGUSR1, "socks-resolve-failure"); + } + goto done; + } + } + } + + /* should we re-use previous active remote address? */ + if (link_socket_actual_defined(&sock->info.lsa->actual)) + { + msg(M_INFO, "TCP/UDP: Preserving recently used remote address: %s", + print_link_socket_actual(&sock->info.lsa->actual, &gc)); + if (remote_dynamic) + { + *remote_dynamic = NULL; + } + } + else + { + CLEAR(sock->info.lsa->actual); + if (sock->info.lsa->current_remote) + { + set_actual_address(&sock->info.lsa->actual, + sock->info.lsa->current_remote); + } + } + +done: + gc_free(&gc); +} + + + +struct link_socket * +link_socket_new(void) +{ + struct link_socket *sock; + + ALLOC_OBJ_CLEAR(sock, struct link_socket); + sock->sd = SOCKET_UNDEFINED; + sock->ctrl_sd = SOCKET_UNDEFINED; + return sock; +} + +void +link_socket_init_phase1(struct context *c, int mode) +{ + struct link_socket *sock = c->c2.link_socket; + struct options *o = &c->options; + ASSERT(sock); + + const char *remote_host = o->ce.remote; + const char *remote_port = o->ce.remote_port; + + sock->local_host = o->ce.local; + sock->local_port = o->ce.local_port; + sock->remote_host = remote_host; + sock->remote_port = remote_port; + sock->dns_cache = c->c1.dns_cache; + sock->http_proxy = c->c1.http_proxy; + sock->socks_proxy = c->c1.socks_proxy; + sock->bind_local = o->ce.bind_local; + sock->resolve_retry_seconds = o->resolve_retry_seconds; + sock->mtu_discover_type = o->ce.mtu_discover_type; + +#ifdef ENABLE_DEBUG + sock->gremlin = o->gremlin; +#endif + + sock->socket_buffer_sizes.rcvbuf = o->rcvbuf; + sock->socket_buffer_sizes.sndbuf = o->sndbuf; + + sock->sockflags = o->sockflags; +#if PORT_SHARE + if (o->port_share_host && o->port_share_port) + { + sock->sockflags |= SF_PORT_SHARE; + } +#endif + sock->mark = o->mark; + sock->bind_dev = o->bind_dev; + + sock->info.proto = o->ce.proto; + sock->info.af = o->ce.af; + sock->info.remote_float = o->ce.remote_float; + sock->info.lsa = &c->c1.link_socket_addr; + sock->info.bind_ipv6_only = o->ce.bind_ipv6_only; + sock->info.ipchange_command = o->ipchange; + sock->info.plugins = c->plugins; + sock->server_poll_timeout = &c->c2.server_poll_interval; + + sock->mode = mode; + if (mode == LS_MODE_TCP_ACCEPT_FROM) + { + ASSERT(c->c2.accept_from); + ASSERT(sock->info.proto == PROTO_TCP_SERVER); + sock->sd = c->c2.accept_from->sd; + /* inherit (possibly guessed) info AF from parent context */ + sock->info.af = c->c2.accept_from->info.af; + } + + /* are we running in HTTP proxy mode? */ + if (sock->http_proxy) + { + ASSERT(sock->info.proto == PROTO_TCP_CLIENT); + + /* the proxy server */ + sock->remote_host = c->c1.http_proxy->options.server; + sock->remote_port = c->c1.http_proxy->options.port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + /* or in Socks proxy mode? */ + else if (sock->socks_proxy) + { + /* the proxy server */ + sock->remote_host = c->c1.socks_proxy->server; + sock->remote_port = c->c1.socks_proxy->port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + else + { + sock->remote_host = remote_host; + sock->remote_port = remote_port; + } + + /* bind behavior for TCP server vs. client */ + if (sock->info.proto == PROTO_TCP_SERVER) + { + if (sock->mode == LS_MODE_TCP_ACCEPT_FROM) + { + sock->bind_local = false; + } + else + { + sock->bind_local = true; + } + } + + if (mode != LS_MODE_TCP_ACCEPT_FROM) + { + if (sock->bind_local) + { + resolve_bind_local(sock, sock->info.af); + } + resolve_remote(sock, 1, NULL, NULL); + } +} + +static void +phase2_set_socket_flags(struct link_socket *sock) +{ + /* set misc socket parameters */ + socket_set_flags(sock->sd, sock->sockflags); + + /* set socket to non-blocking mode */ + set_nonblock(sock->sd); + + /* set Path MTU discovery options on the socket */ + set_mtu_discover_type(sock->sd, sock->mtu_discover_type, sock->info.af); + +#if EXTENDED_SOCKET_ERROR_CAPABILITY + /* if the OS supports it, enable extended error passing on the socket */ + set_sock_extended_error_passing(sock->sd, sock->info.af); +#endif +} + + +static void +linksock_print_addr(struct link_socket *sock) +{ + struct gc_arena gc = gc_new(); + const int msglevel = (sock->mode == LS_MODE_TCP_ACCEPT_FROM) ? D_INIT_MEDIUM : M_INFO; + + /* print local address */ + if (sock->bind_local) + { + sa_family_t ai_family = sock->info.lsa->actual.dest.addr.sa.sa_family; + /* Socket is always bound on the first matching address, + * For bound sockets with no remote addr this is the element of + * the list */ + struct addrinfo *cur; + for (cur = sock->info.lsa->bind_local; cur; cur = cur->ai_next) + { + if (!ai_family || ai_family == cur->ai_family) + { + break; + } + } + ASSERT(cur); + msg(msglevel, "%s link local (bound): %s", + proto2ascii(sock->info.proto, sock->info.af, true), + print_sockaddr(cur->ai_addr, &gc)); + } + else + { + msg(msglevel, "%s link local: (not bound)", + proto2ascii(sock->info.proto, sock->info.af, true)); + } + + /* print active remote address */ + msg(msglevel, "%s link remote: %s", + proto2ascii(sock->info.proto, sock->info.af, true), + print_link_socket_actual_ex(&sock->info.lsa->actual, + ":", + PS_SHOW_PORT_IF_DEFINED, + &gc)); + gc_free(&gc); +} + +static void +phase2_tcp_server(struct link_socket *sock, const char *remote_dynamic, + struct signal_info *sig_info) +{ + volatile int *signal_received = sig_info ? &sig_info->signal_received : NULL; + switch (sock->mode) + { + case LS_MODE_DEFAULT: + sock->sd = socket_listen_accept(sock->sd, + &sock->info.lsa->actual, + remote_dynamic, + sock->info.lsa->bind_local, + true, + false, + signal_received); + break; + + case LS_MODE_TCP_LISTEN: + socket_do_listen(sock->sd, + sock->info.lsa->bind_local, + true, + false); + break; + + case LS_MODE_TCP_ACCEPT_FROM: + sock->sd = socket_do_accept(sock->sd, + &sock->info.lsa->actual, + false); + if (!socket_defined(sock->sd)) + { + register_signal(sig_info, SIGTERM, "socket-undefiled"); + return; + } + tcp_connection_established(&sock->info.lsa->actual); + break; + + default: + ASSERT(0); + } +} + + +static void +phase2_tcp_client(struct link_socket *sock, struct signal_info *sig_info) +{ + bool proxy_retry = false; + do + { + socket_connect(&sock->sd, + sock->info.lsa->current_remote->ai_addr, + get_server_poll_remaining_time(sock->server_poll_timeout), + sig_info); + + if (sig_info->signal_received) + { + return; + } + + if (sock->http_proxy) + { + proxy_retry = establish_http_proxy_passthru(sock->http_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + sock->server_poll_timeout, + &sock->stream_buf.residual, + sig_info); + } + else if (sock->socks_proxy) + { + establish_socks_proxy_passthru(sock->socks_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + sig_info); + } + if (proxy_retry) + { + openvpn_close_socket(sock->sd); + sock->sd = create_socket_tcp(sock->info.lsa->current_remote); + } + + } while (proxy_retry); + +} + +static void +phase2_socks_client(struct link_socket *sock, struct signal_info *sig_info) +{ + socket_connect(&sock->ctrl_sd, + sock->info.lsa->current_remote->ai_addr, + get_server_poll_remaining_time(sock->server_poll_timeout), + sig_info); + + if (sig_info->signal_received) + { + return; + } + + establish_socks_proxy_udpassoc(sock->socks_proxy, + sock->ctrl_sd, + sock->sd, + &sock->socks_relay.dest, + sig_info); + + if (sig_info->signal_received) + { + return; + } + + sock->remote_host = sock->proxy_dest_host; + sock->remote_port = sock->proxy_dest_port; + + addr_zero_host(&sock->info.lsa->actual.dest); + if (sock->info.lsa->remote_list) + { + freeaddrinfo(sock->info.lsa->remote_list); + sock->info.lsa->current_remote = NULL; + sock->info.lsa->remote_list = NULL; + } + + resolve_remote(sock, 1, NULL, sig_info); +} + +#if defined(_WIN32) +static void +create_socket_dco_win(struct context *c, struct link_socket *sock, + struct signal_info *sig_info) +{ + if (!c->c1.tuntap) + { + struct tuntap *tt; + ALLOC_OBJ(tt, struct tuntap); + + *tt = create_dco_handle(c->options.dev_node, &c->gc); + + /* Ensure we can "safely" cast the handle to a socket */ + static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET size differs"); + + c->c1.tuntap = tt; + } + + dco_create_socket(c->c1.tuntap->hand, + sock->info.lsa->current_remote, + sock->bind_local, sock->info.lsa->bind_local, + get_server_poll_remaining_time(sock->server_poll_timeout), + sig_info); + + sock->dco_installed = true; + + if (sig_info->signal_received) + { + return; + } + + sock->sd = (SOCKET)c->c1.tuntap->hand; + linksock_print_addr(sock); +} +#endif /* if defined(_WIN32) */ + +/* finalize socket initialization */ +void +link_socket_init_phase2(struct context *c) +{ + struct link_socket *sock = c->c2.link_socket; + const struct frame *frame = &c->c2.frame; + struct signal_info *sig_info = c->sig; + + const char *remote_dynamic = NULL; + struct signal_info sig_save = {0}; + + ASSERT(sock); + ASSERT(sig_info); + + if (sig_info->signal_received) + { + sig_save = *sig_info; + signal_reset(sig_info); + } + + /* initialize buffers */ + socket_frame_init(frame, sock); + + /* + * Pass a remote name to connect/accept so that + * they can test for dynamic IP address changes + * and throw a SIGUSR1 if appropriate. + */ + if (sock->resolve_retry_seconds) + { + remote_dynamic = sock->remote_host; + } + + /* Second chance to resolv/create socket */ + resolve_remote(sock, 2, &remote_dynamic, sig_info); + + /* If a valid remote has been found, create the socket with its addrinfo */ + if (sock->info.lsa->current_remote) + { +#if defined(_WIN32) + if (dco_enabled(&c->options)) + { + create_socket_dco_win(c, sock, sig_info); + goto done; + } + else +#endif + { + create_socket(sock, sock->info.lsa->current_remote); + } + + } + + /* If socket has not already been created create it now */ + if (sock->sd == SOCKET_UNDEFINED) + { + /* If we have no --remote and have still not figured out the + * protocol family to use we will use the first of the bind */ + + if (sock->bind_local && !sock->remote_host && sock->info.lsa->bind_local) + { + /* Warn if this is because neither v4 or v6 was specified + * and we should not connect a remote */ + if (sock->info.af == AF_UNSPEC) + { + msg(M_WARN, "Could not determine IPv4/IPv6 protocol. Using %s", + addr_family_name(sock->info.lsa->bind_local->ai_family)); + sock->info.af = sock->info.lsa->bind_local->ai_family; + } + + create_socket(sock, sock->info.lsa->bind_local); + } + } + + /* Socket still undefined, give a warning and abort connection */ + if (sock->sd == SOCKET_UNDEFINED) + { + msg(M_WARN, "Could not determine IPv4/IPv6 protocol"); + register_signal(sig_info, SIGUSR1, "Could not determine IPv4/IPv6 protocol"); + goto done; + } + + if (sig_info->signal_received) + { + goto done; + } + + if (sock->info.proto == PROTO_TCP_SERVER) + { + phase2_tcp_server(sock, remote_dynamic, sig_info); + } + else if (sock->info.proto == PROTO_TCP_CLIENT) + { + phase2_tcp_client(sock, sig_info); + + } + else if (sock->info.proto == PROTO_UDP && sock->socks_proxy) + { + phase2_socks_client(sock, sig_info); + } +#ifdef TARGET_ANDROID + if (sock->sd != -1) + { + protect_fd_nonlocal(sock->sd, &sock->info.lsa->actual.dest.addr.sa); + } +#endif + if (sig_info->signal_received) + { + goto done; + } + + phase2_set_socket_flags(sock); + linksock_print_addr(sock); + +done: + if (sig_save.signal_received) + { + /* Always restore the saved signal -- register/throw_signal will handle priority */ + if (sig_save.source == SIG_SOURCE_HARD && sig_info == &siginfo_static) + { + throw_signal(sig_save.signal_received); + } + else + { + register_signal(sig_info, sig_save.signal_received, sig_save.signal_text); + } + } +} + +void +link_socket_close(struct link_socket *sock) +{ + if (sock) + { +#ifdef ENABLE_DEBUG + const int gremlin = GREMLIN_CONNECTION_FLOOD_LEVEL(sock->gremlin); +#else + const int gremlin = 0; +#endif + + if (socket_defined(sock->sd)) + { +#ifdef _WIN32 + close_net_event_win32(&sock->listen_handle, sock->sd, 0); +#endif + if (!gremlin) + { + msg(D_LOW, "TCP/UDP: Closing socket"); + if (openvpn_close_socket(sock->sd)) + { + msg(M_WARN | M_ERRNO, "TCP/UDP: Close Socket failed"); + } + } + sock->sd = SOCKET_UNDEFINED; +#ifdef _WIN32 + if (!gremlin) + { + overlapped_io_close(&sock->reads); + overlapped_io_close(&sock->writes); + } +#endif + } + + if (socket_defined(sock->ctrl_sd)) + { + if (openvpn_close_socket(sock->ctrl_sd)) + { + msg(M_WARN | M_ERRNO, "TCP/UDP: Close Socket (ctrl_sd) failed"); + } + sock->ctrl_sd = SOCKET_UNDEFINED; + } + + stream_buf_close(&sock->stream_buf); + free_buf(&sock->stream_buf_data); + if (!gremlin) + { + free(sock); + } + } +} + +void +setenv_trusted(struct env_set *es, const struct link_socket_info *info) +{ + setenv_link_socket_actual(es, "trusted", &info->lsa->actual, SA_IP_PORT); +} + +static void +ipchange_fmt(const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc) +{ + const char *host = print_sockaddr_ex(&info->lsa->actual.dest.addr.sa, " ", PS_SHOW_PORT, gc); + if (include_cmd) + { + argv_parse_cmd(argv, info->ipchange_command); + argv_printf_cat(argv, "%s", host); + } + else + { + argv_printf(argv, "%s", host); + } + +} + +void +link_socket_connection_initiated(struct link_socket_info *info, + const struct link_socket_actual *act, + const char *common_name, + struct env_set *es) +{ + struct gc_arena gc = gc_new(); + + info->lsa->actual = *act; /* Note: skip this line for --force-dest */ + setenv_trusted(es, info); + info->connection_established = true; + + /* Print connection initiated message, with common name if available */ + { + struct buffer out = alloc_buf_gc(256, &gc); + if (common_name) + { + buf_printf(&out, "[%s] ", common_name); + } + buf_printf(&out, "Peer Connection Initiated with %s", print_link_socket_actual(&info->lsa->actual, &gc)); + msg(M_INFO, "%s", BSTR(&out)); + } + + /* set environmental vars */ + setenv_str(es, "common_name", common_name); + + /* Process --ipchange plugin */ + if (plugin_defined(info->plugins, OPENVPN_PLUGIN_IPCHANGE)) + { + struct argv argv = argv_new(); + ipchange_fmt(false, &argv, info, &gc); + if (plugin_call(info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) + { + msg(M_WARN, "WARNING: ipchange plugin call failed"); + } + argv_free(&argv); + } + + /* Process --ipchange option */ + if (info->ipchange_command) + { + struct argv argv = argv_new(); + setenv_str(es, "script_type", "ipchange"); + ipchange_fmt(true, &argv, info, &gc); + openvpn_run_script(&argv, es, 0, "--ipchange"); + argv_free(&argv); + } + + gc_free(&gc); +} + +void +link_socket_bad_incoming_addr(struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr) +{ + struct gc_arena gc = gc_new(); + struct addrinfo *ai; + + switch (from_addr->dest.addr.sa.sa_family) + { + case AF_INET: + case AF_INET6: + msg(D_LINK_ERRORS, + "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", + print_link_socket_actual(from_addr, &gc), + (int)from_addr->dest.addr.sa.sa_family, + print_sockaddr_ex(info->lsa->remote_list->ai_addr, ":", PS_SHOW_PORT, &gc)); + /* print additional remote addresses */ + for (ai = info->lsa->remote_list->ai_next; ai; ai = ai->ai_next) + { + msg(D_LINK_ERRORS, "or from peer address: %s", + print_sockaddr_ex(ai->ai_addr, ":", PS_SHOW_PORT, &gc)); + } + break; + } + buf->len = 0; + gc_free(&gc); +} + +void +link_socket_bad_outgoing_addr(void) +{ + dmsg(D_READ_WRITE, "TCP/UDP: No outgoing address to send packet"); +} + +in_addr_t +link_socket_current_remote(const struct link_socket_info *info) +{ + const struct link_socket_addr *lsa = info->lsa; + +/* + * This logic supports "redirect-gateway" semantic, which + * makes sense only for PF_INET routes over PF_INET endpoints + * + * Maybe in the future consider PF_INET6 endpoints also ... + * by now just ignore it + * + * For --remote entries with multiple addresses this + * only return the actual endpoint we have successfully connected to + */ + if (lsa->actual.dest.addr.sa.sa_family != AF_INET) + { + return IPV4_INVALID_ADDR; + } + + if (link_socket_actual_defined(&lsa->actual)) + { + return ntohl(lsa->actual.dest.addr.in4.sin_addr.s_addr); + } + else if (lsa->current_remote) + { + return ntohl(((struct sockaddr_in *)lsa->current_remote->ai_addr) + ->sin_addr.s_addr); + } + else + { + return 0; + } +} + +const struct in6_addr * +link_socket_current_remote_ipv6(const struct link_socket_info *info) +{ + const struct link_socket_addr *lsa = info->lsa; + +/* This logic supports "redirect-gateway" semantic, + * for PF_INET6 routes over PF_INET6 endpoints + * + * For --remote entries with multiple addresses this + * only return the actual endpoint we have successfully connected to + */ + if (lsa->actual.dest.addr.sa.sa_family != AF_INET6) + { + return NULL; + } + + if (link_socket_actual_defined(&lsa->actual)) + { + return &(lsa->actual.dest.addr.in6.sin6_addr); + } + else if (lsa->current_remote) + { + return &(((struct sockaddr_in6 *)lsa->current_remote->ai_addr)->sin6_addr); + } + else + { + return NULL; + } +} + +/* + * Return a status string describing socket state. + */ +const char * +socket_stat(const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(64, gc); + if (s) + { + if (rwflags & EVENT_READ) + { + buf_printf(&out, "S%s", + (s->rwflags_debug & EVENT_READ) ? "R" : "r"); +#ifdef _WIN32 + buf_printf(&out, "%s", + overlapped_io_state_ascii(&s->reads)); +#endif + } + if (rwflags & EVENT_WRITE) + { + buf_printf(&out, "S%s", + (s->rwflags_debug & EVENT_WRITE) ? "W" : "w"); +#ifdef _WIN32 + buf_printf(&out, "%s", + overlapped_io_state_ascii(&s->writes)); +#endif + } + } + else + { + buf_printf(&out, "S?"); + } + return BSTR(&out); +} + +/* + * Stream buffer functions, used to packetize a TCP + * stream connection. + */ + +static inline void +stream_buf_reset(struct stream_buf *sb) +{ + dmsg(D_STREAM_DEBUG, "STREAM: RESET"); + sb->residual_fully_formed = false; + sb->buf = sb->buf_init; + buf_reset(&sb->next); + sb->len = -1; +} + +static void +stream_buf_init(struct stream_buf *sb, + struct buffer *buf, + const unsigned int sockflags, + const int proto) +{ + sb->buf_init = *buf; + sb->maxlen = sb->buf_init.len; + sb->buf_init.len = 0; + sb->residual = alloc_buf(sb->maxlen); + sb->error = false; +#if PORT_SHARE + sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCP_SERVER)) + ? PS_ENABLED + : PS_DISABLED; +#endif + stream_buf_reset(sb); + + dmsg(D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen); +} + +static inline void +stream_buf_set_next(struct stream_buf *sb) +{ + /* set up 'next' for next i/o read */ + sb->next = sb->buf; + sb->next.offset = sb->buf.offset + sb->buf.len; + sb->next.len = (sb->len >= 0 ? sb->len : sb->maxlen) - sb->buf.len; + dmsg(D_STREAM_DEBUG, "STREAM: SET NEXT, buf=[%d,%d] next=[%d,%d] len=%d maxlen=%d", + sb->buf.offset, sb->buf.len, + sb->next.offset, sb->next.len, + sb->len, sb->maxlen); + ASSERT(sb->next.len > 0); + ASSERT(buf_safe(&sb->buf, sb->next.len)); +} + +static inline void +stream_buf_get_final(struct stream_buf *sb, struct buffer *buf) +{ + dmsg(D_STREAM_DEBUG, "STREAM: GET FINAL len=%d", + buf_defined(&sb->buf) ? sb->buf.len : -1); + ASSERT(buf_defined(&sb->buf)); + *buf = sb->buf; +} + +static inline void +stream_buf_get_next(struct stream_buf *sb, struct buffer *buf) +{ + dmsg(D_STREAM_DEBUG, "STREAM: GET NEXT len=%d", + buf_defined(&sb->next) ? sb->next.len : -1); + ASSERT(buf_defined(&sb->next)); + *buf = sb->next; +} + +bool +stream_buf_read_setup_dowork(struct link_socket *sock) +{ + if (sock->stream_buf.residual.len && !sock->stream_buf.residual_fully_formed) + { + ASSERT(buf_copy(&sock->stream_buf.buf, &sock->stream_buf.residual)); + ASSERT(buf_init(&sock->stream_buf.residual, 0)); + sock->stream_buf.residual_fully_formed = stream_buf_added(&sock->stream_buf, 0); + dmsg(D_STREAM_DEBUG, "STREAM: RESIDUAL FULLY FORMED [%s], len=%d", + sock->stream_buf.residual_fully_formed ? "YES" : "NO", + sock->stream_buf.residual.len); + } + + if (!sock->stream_buf.residual_fully_formed) + { + stream_buf_set_next(&sock->stream_buf); + } + return !sock->stream_buf.residual_fully_formed; +} + +static bool +stream_buf_added(struct stream_buf *sb, + int length_added) +{ + dmsg(D_STREAM_DEBUG, "STREAM: ADD length_added=%d", length_added); + if (length_added > 0) + { + sb->buf.len += length_added; + } + + /* if length unknown, see if we can get the length prefix from + * the head of the buffer */ + if (sb->len < 0 && sb->buf.len >= (int) sizeof(packet_size_type)) + { + packet_size_type net_size; + +#if PORT_SHARE + if (sb->port_share_state == PS_ENABLED) + { + if (!is_openvpn_protocol(&sb->buf)) + { + msg(D_STREAM_ERRORS, "Non-OpenVPN client protocol detected"); + sb->port_share_state = PS_FOREIGN; + sb->error = true; + return false; + } + else + { + sb->port_share_state = PS_DISABLED; + } + } +#endif + + ASSERT(buf_read(&sb->buf, &net_size, sizeof(net_size))); + sb->len = ntohps(net_size); + + if (sb->len < 1 || sb->len > sb->maxlen) + { + msg(M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]", sb->len, sb->maxlen); + stream_buf_reset(sb); + sb->error = true; + return false; + } + } + + /* is our incoming packet fully read? */ + if (sb->len > 0 && sb->buf.len >= sb->len) + { + /* save any residual data that's part of the next packet */ + ASSERT(buf_init(&sb->residual, 0)); + if (sb->buf.len > sb->len) + { + ASSERT(buf_copy_excess(&sb->residual, &sb->buf, sb->len)); + } + dmsg(D_STREAM_DEBUG, "STREAM: ADD returned TRUE, buf_len=%d, residual_len=%d", + BLEN(&sb->buf), + BLEN(&sb->residual)); + return true; + } + else + { + dmsg(D_STREAM_DEBUG, "STREAM: ADD returned FALSE (have=%d need=%d)", sb->buf.len, sb->len); + stream_buf_set_next(sb); + return false; + } +} + +static void +stream_buf_close(struct stream_buf *sb) +{ + free_buf(&sb->residual); +} + +/* + * The listen event is a special event whose sole purpose is + * to tell us that there's a new incoming connection on a + * TCP socket, for use in server mode. + */ +event_t +socket_listen_event_handle(struct link_socket *s) +{ +#ifdef _WIN32 + if (!defined_net_event_win32(&s->listen_handle)) + { + init_net_event_win32(&s->listen_handle, FD_ACCEPT, s->sd, 0); + } + return &s->listen_handle; +#else /* ifdef _WIN32 */ + return s->sd; +#endif +} + +/* + * Format IP addresses in ascii + */ + +const char * +print_sockaddr_ex(const struct sockaddr *sa, + const char *separator, + const unsigned int flags, + struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(128, gc); + bool addr_is_defined = false; + char hostaddr[NI_MAXHOST] = ""; + char servname[NI_MAXSERV] = ""; + int status; + + socklen_t salen = 0; + switch (sa->sa_family) + { + case AF_INET: + if (!(flags & PS_DONT_SHOW_FAMILY)) + { + buf_puts(&out, "[AF_INET]"); + } + salen = sizeof(struct sockaddr_in); + addr_is_defined = ((struct sockaddr_in *) sa)->sin_addr.s_addr != 0; + break; + + case AF_INET6: + if (!(flags & PS_DONT_SHOW_FAMILY)) + { + buf_puts(&out, "[AF_INET6]"); + } + salen = sizeof(struct sockaddr_in6); + addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) sa)->sin6_addr); + break; + + case AF_UNSPEC: + if (!(flags & PS_DONT_SHOW_FAMILY)) + { + return "[AF_UNSPEC]"; + } + else + { + return ""; + } + + default: + ASSERT(0); + } + + status = getnameinfo(sa, salen, hostaddr, sizeof(hostaddr), + servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV); + + if (status!=0) + { + buf_printf(&out, "[nameinfo() err: %s]", gai_strerror(status)); + return BSTR(&out); + } + + if (!(flags & PS_DONT_SHOW_ADDR)) + { + if (addr_is_defined) + { + buf_puts(&out, hostaddr); + } + else + { + buf_puts(&out, "[undef]"); + } + } + + if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED)) + { + if (separator) + { + buf_puts(&out, separator); + } + + buf_puts(&out, servname); + } + + return BSTR(&out); +} + +const char * +print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena *gc) +{ + return print_link_socket_actual_ex(act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc); +} + +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + +const char * +print_link_socket_actual_ex(const struct link_socket_actual *act, + const char *separator, + const unsigned int flags, + struct gc_arena *gc) +{ + if (act) + { + struct buffer out = alloc_buf_gc(128, gc); + buf_printf(&out, "%s", print_sockaddr_ex(&act->dest.addr.sa, separator, flags, gc)); +#if ENABLE_IP_PKTINFO + char ifname[IF_NAMESIZE] = "[undef]"; + + if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act)) + { + switch (act->dest.addr.sa.sa_family) + { + case AF_INET: + { + struct openvpn_sockaddr sa; + CLEAR(sa); + sa.addr.in4.sin_family = AF_INET; +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst; + if_indextoname(act->pi.in4.ipi_ifindex, ifname); +#elif defined(IP_RECVDSTADDR) + sa.addr.in4.sin_addr = act->pi.in4; + ifname[0] = 0; +#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + buf_printf(&out, " (via %s%%%s)", + print_sockaddr_ex(&sa.addr.sa, separator, 0, gc), + ifname); + } + break; + + case AF_INET6: + { + struct sockaddr_in6 sin6; + char buf[INET6_ADDRSTRLEN] = "[undef]"; + CLEAR(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = act->pi.in6.ipi6_addr; + if_indextoname(act->pi.in6.ipi6_ifindex, ifname); + if (getnameinfo((struct sockaddr *)&sin6, sizeof(struct sockaddr_in6), + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) == 0) + { + buf_printf(&out, " (via %s%%%s)", buf, ifname); + } + else + { + buf_printf(&out, " (via [getnameinfo() err]%%%s)", ifname); + } + } + break; + } + } +#endif /* if ENABLE_IP_PKTINFO */ + return BSTR(&out); + } + else + { + return "[NULL]"; + } +} + +/* + * Convert an in_addr_t in host byte order + * to an ascii dotted quad. + */ +const char * +print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc) +{ + struct in_addr ia; + struct buffer out = alloc_buf_gc(64, gc); + + if (addr || !(flags & IA_EMPTY_IF_UNDEF)) + { + CLEAR(ia); + ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl(addr); + + buf_printf(&out, "%s", inet_ntoa(ia)); + } + return BSTR(&out); +} + +/* + * Convert an in6_addr in host byte order + * to an ascii representation of an IPv6 address + */ +const char * +print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(64, gc); + char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */ + + if (memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 + || !(flags & IA_EMPTY_IF_UNDEF)) + { + inet_ntop(AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1); + buf_printf(&out, "%s", tmp_out_buf ); + } + return BSTR(&out); +} + +/* + * Convert an in_port_t in host byte order to a string + */ +const char * +print_in_port_t(in_port_t port, struct gc_arena *gc) +{ + struct buffer buffer = alloc_buf_gc(8, gc); + buf_printf(&buffer, "%hu", port); + return BSTR(&buffer); +} + +#ifndef UINT8_MAX +#define UINT8_MAX 0xff +#endif + +/* add some offset to an ipv6 address + * (add in steps of 8 bits, taking overflow into next round) + */ +struct in6_addr +add_in6_addr( struct in6_addr base, uint32_t add ) +{ + int i; + + for (i = 15; i>=0 && add > 0; i--) + { + register int carry; + register uint32_t h; + + h = (unsigned char) base.s6_addr[i]; + base.s6_addr[i] = (h+add) & UINT8_MAX; + + /* using explicit carry for the 8-bit additions will catch + * 8-bit and(!) 32-bit overruns nicely + */ + carry = ((h & 0xff) + (add & 0xff)) >> 8; + add = (add>>8) + carry; + } + return base; +} + +/* set environmental variables for ip/port in *addr */ +void +setenv_sockaddr(struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const unsigned int flags) +{ + char name_buf[256]; + + char buf[128]; + switch (addr->addr.sa.sa_family) + { + case AF_INET: + if (flags & SA_IP_PORT) + { + openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix); + } + else + { + openvpn_snprintf(name_buf, sizeof(name_buf), "%s", name_prefix); + } + + setenv_str(es, name_buf, inet_ntoa(addr->addr.in4.sin_addr)); + + if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port) + { + openvpn_snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix); + setenv_int(es, name_buf, ntohs(addr->addr.in4.sin_port)); + } + break; + + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED( &addr->addr.in6.sin6_addr )) + { + struct in_addr ia; + memcpy(&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12], + sizeof(ia.s_addr)); + openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix); + openvpn_snprintf(buf, sizeof(buf), "%s", inet_ntoa(ia) ); + } + else + { + openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip6", name_prefix); + getnameinfo(&addr->addr.sa, sizeof(struct sockaddr_in6), + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); + } + setenv_str(es, name_buf, buf); + + if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port) + { + openvpn_snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix); + setenv_int(es, name_buf, ntohs(addr->addr.in6.sin6_port)); + } + break; + } +} + +void +setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t addr, const unsigned int flags) +{ + if (addr || !(flags & SA_SET_IF_NONZERO)) + { + struct openvpn_sockaddr si; + CLEAR(si); + si.addr.in4.sin_family = AF_INET; + si.addr.in4.sin_addr.s_addr = htonl(addr); + setenv_sockaddr(es, name_prefix, &si, flags); + } +} + +void +setenv_in6_addr(struct env_set *es, + const char *name_prefix, + const struct in6_addr *addr, + const unsigned int flags) +{ + if (!IN6_IS_ADDR_UNSPECIFIED(addr) || !(flags & SA_SET_IF_NONZERO)) + { + struct openvpn_sockaddr si; + CLEAR(si); + si.addr.in6.sin6_family = AF_INET6; + si.addr.in6.sin6_addr = *addr; + setenv_sockaddr(es, name_prefix, &si, flags); + } +} + +void +setenv_link_socket_actual(struct env_set *es, + const char *name_prefix, + const struct link_socket_actual *act, + const unsigned int flags) +{ + setenv_sockaddr(es, name_prefix, &act->dest, flags); +} + +/* + * Convert protocol names between index and ascii form. + */ + +struct proto_names { + const char *short_form; + const char *display_form; + sa_family_t proto_af; + int proto; +}; + +/* Indexed by PROTO_x */ +static const struct proto_names proto_names[] = { + {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE}, + /* try IPv4 and IPv6 (client), bind dual-stack (server) */ + {"udp", "UDP", AF_UNSPEC, PROTO_UDP}, + {"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER}, + {"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT}, + {"tcp", "TCP", AF_UNSPEC, PROTO_TCP}, + /* force IPv4 */ + {"udp4", "UDPv4", AF_INET, PROTO_UDP}, + {"tcp4-server", "TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER}, + {"tcp4-client", "TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT}, + {"tcp4", "TCPv4", AF_INET, PROTO_TCP}, + /* force IPv6 */ + {"udp6", "UDPv6", AF_INET6, PROTO_UDP}, + {"tcp6-server", "TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER}, + {"tcp6-client", "TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT}, + {"tcp6", "TCPv6", AF_INET6, PROTO_TCP}, +}; + +int +ascii2proto(const char *proto_name) +{ + int i; + for (i = 0; i < SIZE(proto_names); ++i) + { + if (!strcmp(proto_name, proto_names[i].short_form)) + { + return proto_names[i].proto; + } + } + return -1; +} + +sa_family_t +ascii2af(const char *proto_name) +{ + int i; + for (i = 0; i < SIZE(proto_names); ++i) + { + if (!strcmp(proto_name, proto_names[i].short_form)) + { + return proto_names[i].proto_af; + } + } + return 0; +} + +const char * +proto2ascii(int proto, sa_family_t af, bool display_form) +{ + unsigned int i; + for (i = 0; i < SIZE(proto_names); ++i) + { + if (proto_names[i].proto_af == af && proto_names[i].proto == proto) + { + if (display_form) + { + return proto_names[i].display_form; + } + else + { + return proto_names[i].short_form; + } + } + } + + return "[unknown protocol]"; +} + +const char * +proto2ascii_all(struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(256, gc); + int i; + + for (i = 0; i < SIZE(proto_names); ++i) + { + if (i) + { + buf_printf(&out, " "); + } + buf_printf(&out, "[%s]", proto_names[i].short_form); + } + return BSTR(&out); +} + +const char * +addr_family_name(int af) +{ + switch (af) + { + case AF_INET: return "AF_INET"; + + case AF_INET6: return "AF_INET6"; + } + return "AF_UNSPEC"; +} + +/* + * Given a local proto, return local proto + * if !remote, or compatible remote proto + * if remote. + * + * This is used for options compatibility + * checking. + * + * IPv6 and IPv4 protocols are comptabile but OpenVPN + * has always sent UDPv4, TCPv4 over the wire. Keep these + * strings for backward compatibility + */ +const char * +proto_remote(int proto, bool remote) +{ + ASSERT(proto >= 0 && proto < PROTO_N); + if (proto == PROTO_UDP) + { + return "UDPv4"; + } + + if ( (remote && proto == PROTO_TCP_CLIENT) + || (!remote && proto == PROTO_TCP_SERVER)) + { + return "TCPv4_SERVER"; + } + if ( (remote && proto == PROTO_TCP_SERVER) + || (!remote && proto == PROTO_TCP_CLIENT)) + { + return "TCPv4_CLIENT"; + } + + ASSERT(0); + return ""; /* Make the compiler happy */ +} + +/* + * Bad incoming address lengths that differ from what + * we expect are considered to be fatal errors. + */ +void +bad_address_length(int actual, int expected) +{ + msg(M_FATAL, "ERROR: received strange incoming packet with an address length of %d -- we only accept address lengths of %d.", + actual, + expected); +} + +/* + * Socket Read Routines + */ + +int +link_socket_read_tcp(struct link_socket *sock, + struct buffer *buf) +{ + int len = 0; + + if (!sock->stream_buf.residual_fully_formed) + { + /* with Linux-DCO, we sometimes try to access a socket that is + * already installed in the kernel and has no valid file descriptor + * anymore. This is a bug. + * Handle by resetting client instance instead of crashing. + */ + if (sock->sd == SOCKET_UNDEFINED) + { + msg(M_INFO, "BUG: link_socket_read_tcp(): sock->sd==-1, reset client instance" ); + sock->stream_reset = true; /* reset client instance */ + return buf->len = 0; /* nothing to read */ + } + +#ifdef _WIN32 + sockethandle_t sh = { .s = sock->sd }; + len = sockethandle_finalize(sh, &sock->reads, buf, NULL); +#else + struct buffer frag; + stream_buf_get_next(&sock->stream_buf, &frag); + len = recv(sock->sd, BPTR(&frag), BLEN(&frag), MSG_NOSIGNAL); +#endif + + if (!len) + { + sock->stream_reset = true; + } + if (len <= 0) + { + return buf->len = len; + } + } + + if (sock->stream_buf.residual_fully_formed + || stream_buf_added(&sock->stream_buf, len)) /* packet complete? */ + { + stream_buf_get_final(&sock->stream_buf, buf); + stream_buf_reset(&sock->stream_buf); + return buf->len; + } + else + { + return buf->len = 0; /* no error, but packet is still incomplete */ + } +} + +#ifndef _WIN32 + +#if ENABLE_IP_PKTINFO + +/* make the buffer large enough to handle ancillary socket data for + * both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292) + */ +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) +#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof(struct in6_pktinfo)), \ + CMSG_SPACE(sizeof(struct in_pktinfo)) ) +#else +#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof(struct in6_pktinfo)), \ + CMSG_SPACE(sizeof(struct in_addr)) ) +#endif + +static socklen_t +link_socket_read_udp_posix_recvmsg(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from) +{ + struct iovec iov; + uint8_t pktinfo_buf[PKTINFO_BUF_SIZE]; + struct msghdr mesg; + socklen_t fromlen = sizeof(from->dest.addr); + + ASSERT(sock->sd >= 0); /* can't happen */ + + iov.iov_base = BPTR(buf); + iov.iov_len = buf_forward_capacity_total(buf); + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; + mesg.msg_name = &from->dest.addr; + mesg.msg_namelen = fromlen; + mesg.msg_control = pktinfo_buf; + mesg.msg_controllen = sizeof pktinfo_buf; + buf->len = recvmsg(sock->sd, &mesg, 0); + if (buf->len >= 0) + { + struct cmsghdr *cmsg; + fromlen = mesg.msg_namelen; + cmsg = CMSG_FIRSTHDR(&mesg); + if (cmsg != NULL + && CMSG_NXTHDR(&mesg, cmsg) == NULL +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + && cmsg->cmsg_level == SOL_IP + && cmsg->cmsg_type == IP_PKTINFO + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_pktinfo)) ) +#elif defined(IP_RECVDSTADDR) + && cmsg->cmsg_level == IPPROTO_IP + && cmsg->cmsg_type == IP_RECVDSTADDR + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_addr)) ) +#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + { +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA(cmsg); + from->pi.in4.ipi_ifindex = pkti->ipi_ifindex; + from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; +#elif defined(IP_RECVDSTADDR) + from->pi.in4 = *(struct in_addr *) CMSG_DATA(cmsg); +#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + } + else if (cmsg != NULL + && CMSG_NXTHDR(&mesg, cmsg) == NULL + && cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in6_pktinfo)) ) + { + struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex; + from->pi.in6.ipi6_addr = pkti6->ipi6_addr; + } + else if (cmsg != NULL) + { + msg(M_WARN, "CMSG received that cannot be parsed (cmsg_level=%d, cmsg_type=%d, cmsg=len=%d)", (int)cmsg->cmsg_level, (int)cmsg->cmsg_type, (int)cmsg->cmsg_len ); + } + } + + return fromlen; +} +#endif /* if ENABLE_IP_PKTINFO */ + +int +link_socket_read_udp_posix(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from) +{ + socklen_t fromlen = sizeof(from->dest.addr); + socklen_t expectedlen = af_addr_size(sock->info.af); + addr_zero_host(&from->dest); + + ASSERT(sock->sd >= 0); /* can't happen */ + +#if ENABLE_IP_PKTINFO + /* Both PROTO_UDPv4 and PROTO_UDPv6 */ + if (sock->info.proto == PROTO_UDP && sock->sockflags & SF_USE_IP_PKTINFO) + { + fromlen = link_socket_read_udp_posix_recvmsg(sock, buf, from); + } + else +#endif + buf->len = recvfrom(sock->sd, BPTR(buf), buf_forward_capacity(buf), 0, + &from->dest.addr.sa, &fromlen); + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ + if (buf->len >= 0 && expectedlen && fromlen != expectedlen) + { + bad_address_length(fromlen, expectedlen); + } + return buf->len; +} + +#endif /* ifndef _WIN32 */ + +/* + * Socket Write Routines + */ + +int +link_socket_write_tcp(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + packet_size_type len = BLEN(buf); + dmsg(D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset); + ASSERT(len <= sock->stream_buf.maxlen); + len = htonps(len); + + uint8_t opcode = *BPTR(buf) >> P_OPCODE_SHIFT; + + ASSERT(buf_write_prepend(buf, &len, sizeof(len))); + + if (sock->sockflags & SF_TCP_SPLITRESET) + { + int saved_len; + int size; + if (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 + || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) + { + saved_len = buf->len; + buf->len = 1; + + socket_set_tcp_nodelay(sock->sd, 1); +#ifdef _WIN32 + size = link_socket_write_win32(sock, buf, to); +#else + size = link_socket_write_tcp_posix(sock, buf, to); +#endif + if (!(sock->sockflags & SF_TCP_NODELAY)) + { + socket_set_tcp_nodelay(sock->sd, 0); + } + buf->len = saved_len; + buf_advance(buf, 1); + +#ifdef _WIN32 + size += link_socket_write_win32(sock, buf, to); +#else + size += link_socket_write_tcp_posix(sock, buf, to); +#endif + return size; + } + } + +#ifdef _WIN32 + return link_socket_write_win32(sock, buf, to); +#else + return link_socket_write_tcp_posix(sock, buf, to); +#endif +} + +#if ENABLE_IP_PKTINFO + +size_t +link_socket_write_udp_posix_sendmsg(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + struct iovec iov; + struct msghdr mesg; + struct cmsghdr *cmsg; + uint8_t pktinfo_buf[PKTINFO_BUF_SIZE]; + + iov.iov_base = BPTR(buf); + iov.iov_len = BLEN(buf); + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; + switch (to->dest.addr.sa.sa_family) + { + case AF_INET: + { + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof(struct sockaddr_in); + mesg.msg_control = pktinfo_buf; + mesg.msg_flags = 0; +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + mesg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + cmsg = CMSG_FIRSTHDR(&mesg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + { + struct in_pktinfo *pkti; + pkti = (struct in_pktinfo *) CMSG_DATA(cmsg); + pkti->ipi_ifindex = to->pi.in4.ipi_ifindex; + pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst; + pkti->ipi_addr.s_addr = 0; + } +#elif defined(IP_RECVDSTADDR) + ASSERT( CMSG_SPACE(sizeof(struct in_addr)) <= sizeof(pktinfo_buf) ); + mesg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + cmsg = CMSG_FIRSTHDR(&mesg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_RECVDSTADDR; + *(struct in_addr *) CMSG_DATA(cmsg) = to->pi.in4; +#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ + break; + } + + case AF_INET6: + { + struct in6_pktinfo *pkti6; + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof(struct sockaddr_in6); + + ASSERT( CMSG_SPACE(sizeof(struct in6_pktinfo)) <= sizeof(pktinfo_buf) ); + mesg.msg_control = pktinfo_buf; + mesg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + mesg.msg_flags = 0; + cmsg = CMSG_FIRSTHDR(&mesg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pkti6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex; + pkti6->ipi6_addr = to->pi.in6.ipi6_addr; + break; + } + + default: ASSERT(0); + } + return sendmsg(sock->sd, &mesg, 0); +} + +#endif /* if ENABLE_IP_PKTINFO */ + +/* + * Win32 overlapped socket I/O functions. + */ + +#ifdef _WIN32 + +static int +socket_get_last_error(const struct link_socket *sock) +{ + if (sock->dco_installed) + { + return GetLastError(); + } + + return WSAGetLastError(); +} + +int +socket_recv_queue(struct link_socket *sock, int maxsize) +{ + if (sock->reads.iostate == IOSTATE_INITIAL) + { + WSABUF wsabuf[1]; + int status; + + /* reset buf to its initial state */ + if (proto_is_udp(sock->info.proto)) + { + sock->reads.buf = sock->reads.buf_init; + } + else if (proto_is_tcp(sock->info.proto)) + { + stream_buf_get_next(&sock->stream_buf, &sock->reads.buf); + } + else + { + ASSERT(0); + } + + /* Win32 docs say it's okay to allocate the wsabuf on the stack */ + wsabuf[0].buf = BSTR(&sock->reads.buf); + wsabuf[0].len = maxsize ? maxsize : BLEN(&sock->reads.buf); + + /* check for buffer overflow */ + ASSERT(wsabuf[0].len <= BLEN(&sock->reads.buf)); + + /* the overlapped read will signal this event on I/O completion */ + ASSERT(ResetEvent(sock->reads.overlapped.hEvent)); + sock->reads.flags = 0; + + if (sock->dco_installed) + { + status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len, + &sock->reads.size, &sock->reads.overlapped); + /* Readfile status is inverted from WSARecv */ + status = !status; + } + else if (proto_is_udp(sock->info.proto)) + { + sock->reads.addr_defined = true; + sock->reads.addrlen = sizeof(sock->reads.addr6); + status = WSARecvFrom( + sock->sd, + wsabuf, + 1, + &sock->reads.size, + &sock->reads.flags, + (struct sockaddr *) &sock->reads.addr, + &sock->reads.addrlen, + &sock->reads.overlapped, + NULL); + } + else if (proto_is_tcp(sock->info.proto)) + { + sock->reads.addr_defined = false; + status = WSARecv( + sock->sd, + wsabuf, + 1, + &sock->reads.size, + &sock->reads.flags, + &sock->reads.overlapped, + NULL); + } + else + { + status = 0; + ASSERT(0); + } + + if (!status) /* operation completed immediately? */ + { + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ + int af_len = af_addr_size(sock->info.af); + if (sock->reads.addr_defined && af_len && sock->reads.addrlen != af_len) + { + bad_address_length(sock->reads.addrlen, af_len); + } + sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; + + /* since we got an immediate return, we must signal the event object ourselves */ + ASSERT(SetEvent(sock->reads.overlapped.hEvent)); + sock->reads.status = 0; + + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive immediate return [%d,%d]", + (int) wsabuf[0].len, + (int) sock->reads.size); + } + else + { + status = socket_get_last_error(sock); + if (status == WSA_IO_PENDING) /* operation queued? */ + { + sock->reads.iostate = IOSTATE_QUEUED; + sock->reads.status = status; + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive queued [%d]", + (int) wsabuf[0].len); + } + else /* error occurred */ + { + struct gc_arena gc = gc_new(); + ASSERT(SetEvent(sock->reads.overlapped.hEvent)); + sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; + sock->reads.status = status; + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive error [%d]: %s", + (int) wsabuf[0].len, + strerror_win32(status, &gc)); + gc_free(&gc); + } + } + } + return sock->reads.iostate; +} + +int +socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct link_socket_actual *to) +{ + if (sock->writes.iostate == IOSTATE_INITIAL) + { + WSABUF wsabuf[1]; + int status; + + /* make a private copy of buf */ + sock->writes.buf = sock->writes.buf_init; + sock->writes.buf.len = 0; + ASSERT(buf_copy(&sock->writes.buf, buf)); + + /* Win32 docs say it's okay to allocate the wsabuf on the stack */ + wsabuf[0].buf = BSTR(&sock->writes.buf); + wsabuf[0].len = BLEN(&sock->writes.buf); + + /* the overlapped write will signal this event on I/O completion */ + ASSERT(ResetEvent(sock->writes.overlapped.hEvent)); + sock->writes.flags = 0; + + if (sock->dco_installed) + { + status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len, + &sock->writes.size, &sock->writes.overlapped); + + /* WriteFile status is inverted from WSASendTo */ + status = !status; + + } + else if (proto_is_udp(sock->info.proto)) + { + /* set destination address for UDP writes */ + sock->writes.addr_defined = true; + if (to->dest.addr.sa.sa_family == AF_INET6) + { + sock->writes.addr6 = to->dest.addr.in6; + sock->writes.addrlen = sizeof(sock->writes.addr6); + } + else + { + sock->writes.addr = to->dest.addr.in4; + sock->writes.addrlen = sizeof(sock->writes.addr); + } + + status = WSASendTo( + sock->sd, + wsabuf, + 1, + &sock->writes.size, + sock->writes.flags, + (struct sockaddr *) &sock->writes.addr, + sock->writes.addrlen, + &sock->writes.overlapped, + NULL); + } + else if (proto_is_tcp(sock->info.proto)) + { + /* destination address for TCP writes was established on connection initiation */ + sock->writes.addr_defined = false; + + status = WSASend( + sock->sd, + wsabuf, + 1, + &sock->writes.size, + sock->writes.flags, + &sock->writes.overlapped, + NULL); + } + else + { + status = 0; + ASSERT(0); + } + + if (!status) /* operation completed immediately? */ + { + sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN; + + /* since we got an immediate return, we must signal the event object ourselves */ + ASSERT(SetEvent(sock->writes.overlapped.hEvent)); + + sock->writes.status = 0; + + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send immediate return [%d,%d]", + (int) wsabuf[0].len, + (int) sock->writes.size); + } + else + { + status = socket_get_last_error(sock); + /* both status code have the identical value */ + if (status == WSA_IO_PENDING || status == ERROR_IO_PENDING) /* operation queued? */ + { + sock->writes.iostate = IOSTATE_QUEUED; + sock->writes.status = status; + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send queued [%d]", + (int) wsabuf[0].len); + } + else /* error occurred */ + { + struct gc_arena gc = gc_new(); + ASSERT(SetEvent(sock->writes.overlapped.hEvent)); + sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN; + sock->writes.status = status; + + dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send error [%d]: %s", + (int) wsabuf[0].len, + strerror_win32(status, &gc)); + + gc_free(&gc); + } + } + } + return sock->writes.iostate; +} + +/* Returns the number of bytes successfully read */ +int +sockethandle_finalize(sockethandle_t sh, + struct overlapped_io *io, + struct buffer *buf, + struct link_socket_actual *from) +{ + int ret = -1; + BOOL status; + + switch (io->iostate) + { + case IOSTATE_QUEUED: + status = SocketHandleGetOverlappedResult(sh, io); + if (status) + { + /* successful return for a queued operation */ + if (buf) + { + *buf = io->buf; + } + ret = io->size; + io->iostate = IOSTATE_INITIAL; + ASSERT(ResetEvent(io->overlapped.hEvent)); + + dmsg(D_WIN32_IO, "WIN32 I/O: Completion success [%d]", ret); + } + else + { + /* error during a queued operation */ + ret = -1; + if (SocketHandleGetLastError(sh) != ERROR_IO_INCOMPLETE) + { + /* if no error (i.e. just not finished yet), then DON'T execute this code */ + io->iostate = IOSTATE_INITIAL; + ASSERT(ResetEvent(io->overlapped.hEvent)); + msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Completion error"); + } + } + break; + + case IOSTATE_IMMEDIATE_RETURN: + io->iostate = IOSTATE_INITIAL; + ASSERT(ResetEvent(io->overlapped.hEvent)); + if (io->status) + { + /* error return for a non-queued operation */ + SocketHandleSetLastError(sh, io->status); + ret = -1; + msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Completion non-queued error"); + } + else + { + /* successful return for a non-queued operation */ + if (buf) + { + *buf = io->buf; + } + ret = io->size; + dmsg(D_WIN32_IO, "WIN32 I/O: Completion non-queued success [%d]", ret); + } + break; + + case IOSTATE_INITIAL: /* were we called without proper queueing? */ + SocketHandleSetInvalError(sh); + ret = -1; + dmsg(D_WIN32_IO, "WIN32 I/O: Completion BAD STATE"); + break; + + default: + ASSERT(0); + } + + /* return from address if requested */ + if (!sh.is_handle && from) + { + if (ret >= 0 && io->addr_defined) + { + /* TODO(jjo): streamline this mess */ + /* in this func we don't have relevant info about the PF_ of this + * endpoint, as link_socket_actual will be zero for the 1st received packet + * + * Test for inets PF_ possible sizes + */ + switch (io->addrlen) + { + case sizeof(struct sockaddr_in): + case sizeof(struct sockaddr_in6): + /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 + * under _WIN32*/ + case sizeof(struct sockaddr_in6)-4: + break; + + default: + bad_address_length(io->addrlen, af_addr_size(io->addr.sin_family)); + } + + switch (io->addr.sin_family) + { + case AF_INET: + from->dest.addr.in4 = io->addr; + break; + + case AF_INET6: + from->dest.addr.in6 = io->addr6; + break; + } + } + else + { + CLEAR(from->dest.addr); + } + } + + if (buf) + { + buf->len = ret; + } + return ret; +} + +#endif /* _WIN32 */ + +/* + * Socket event notification + */ + +unsigned int +socket_set(struct link_socket *s, + struct event_set *es, + unsigned int rwflags, + void *arg, + unsigned int *persistent) +{ + if (s) + { + if ((rwflags & EVENT_READ) && !stream_buf_read_setup(s)) + { + ASSERT(!persistent); + rwflags &= ~EVENT_READ; + } + +#ifdef _WIN32 + if (rwflags & EVENT_READ) + { + socket_recv_queue(s, 0); + } +#endif + + /* if persistent is defined, call event_ctl only if rwflags has changed since last call */ + if (!persistent || *persistent != rwflags) + { + event_ctl(es, socket_event_handle(s), rwflags, arg); + if (persistent) + { + *persistent = rwflags; + } + } + + s->rwflags_debug = rwflags; + } + return rwflags; +} + +void +sd_close(socket_descriptor_t *sd) +{ + if (sd && socket_defined(*sd)) + { + openvpn_close_socket(*sd); + *sd = SOCKET_UNDEFINED; + } +} + +#if UNIX_SOCK_SUPPORT + +/* + * code for unix domain sockets + */ + +const char * +sockaddr_unix_name(const struct sockaddr_un *local, const char *null) +{ + if (local && local->sun_family == PF_UNIX) + { + return local->sun_path; + } + else + { + return null; + } +} + +socket_descriptor_t +create_socket_unix(void) +{ + socket_descriptor_t sd; + + if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + { + msg(M_ERR, "Cannot create unix domain socket"); + } + + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(sd); + + return sd; +} + +void +socket_bind_unix(socket_descriptor_t sd, + struct sockaddr_un *local, + const char *prefix) +{ + struct gc_arena gc = gc_new(); + const mode_t orig_umask = umask(0); + + if (bind(sd, (struct sockaddr *) local, sizeof(struct sockaddr_un))) + { + msg(M_FATAL | M_ERRNO, + "%s: Socket bind[%d] failed on unix domain socket %s", + prefix, + (int)sd, + sockaddr_unix_name(local, "NULL")); + } + + umask(orig_umask); + gc_free(&gc); +} + +socket_descriptor_t +socket_accept_unix(socket_descriptor_t sd, + struct sockaddr_un *remote) +{ + socklen_t remote_len = sizeof(struct sockaddr_un); + socket_descriptor_t ret; + + CLEAR(*remote); + ret = accept(sd, (struct sockaddr *) remote, &remote_len); + if (ret >= 0) + { + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(ret); + } + return ret; +} + +int +socket_connect_unix(socket_descriptor_t sd, + struct sockaddr_un *remote) +{ + int status = connect(sd, (struct sockaddr *) remote, sizeof(struct sockaddr_un)); + if (status) + { + status = openvpn_errno(); + } + return status; +} + +void +sockaddr_unix_init(struct sockaddr_un *local, const char *path) +{ + local->sun_family = PF_UNIX; + strncpynt(local->sun_path, path, sizeof(local->sun_path)); +} + +void +socket_delete_unix(const struct sockaddr_un *local) +{ + const char *name = sockaddr_unix_name(local, NULL); + if (name && strlen(name)) + { + unlink(name); + } +} + +bool +unix_socket_get_peer_uid_gid(const socket_descriptor_t sd, int *uid, int *gid) +{ +#ifdef HAVE_GETPEEREID + uid_t u; + gid_t g; + if (getpeereid(sd, &u, &g) == -1) + { + return false; + } + if (uid) + { + *uid = u; + } + if (gid) + { + *gid = g; + } + return true; +#elif defined(SO_PEERCRED) + struct ucred peercred; + socklen_t so_len = sizeof(peercred); + if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) == -1) + { + return false; + } + if (uid) + { + *uid = peercred.uid; + } + if (gid) + { + *gid = peercred.gid; + } + return true; +#else /* ifdef HAVE_GETPEEREID */ + return false; +#endif /* ifdef HAVE_GETPEEREID */ +} + +#endif /* if UNIX_SOCK_SUPPORT */ diff --git a/tools/deps/custom_openvpn/src/openvpn/socket.h b/tools/deps/custom_openvpn/src/openvpn/socket.h new file mode 100644 index 000000000..30fc9d177 --- /dev/null +++ b/tools/deps/custom_openvpn/src/openvpn/socket.h @@ -0,0 +1,1321 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2023 OpenVPN Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include "buffer.h" +#include "common.h" +#include "error.h" +#include "proto.h" +#include "mtu.h" +#include "win32.h" +#include "event.h" +#include "proxy.h" +#include "socks.h" +#include "misc.h" +#include "tun.h" + +/* + * OpenVPN's default port number as assigned by IANA. + */ +#define OPENVPN_PORT "1194" + +/* + * Number of seconds that "resolv-retry infinite" + * represents. + */ +#define RESOLV_RETRY_INFINITE 1000000000 + +/* + * packet_size_type is used to communicate packet size + * over the wire when stream oriented protocols are + * being used + */ + +typedef uint16_t packet_size_type; + +/* convert a packet_size_type from host to network order */ +#define htonps(x) htons(x) + +/* convert a packet_size_type from network to host order */ +#define ntohps(x) ntohs(x) + +/* OpenVPN sockaddr struct */ +struct openvpn_sockaddr +{ + /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */ + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + } addr; +}; + +/* struct to hold preresolved host names */ +struct cached_dns_entry { + const char *hostname; + const char *servname; + int ai_family; + int flags; + struct addrinfo *ai; + struct cached_dns_entry *next; +}; + +/* actual address of remote, based on source address of received packets */ +struct link_socket_actual +{ + /*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */ + + struct openvpn_sockaddr dest; +#if ENABLE_IP_PKTINFO + union { +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + struct in_pktinfo in4; +#elif defined(IP_RECVDSTADDR) + struct in_addr in4; +#endif + struct in6_pktinfo in6; + } pi; +#endif +}; + +/* IP addresses which are persistent across SIGUSR1s */ +struct link_socket_addr +{ + struct addrinfo *bind_local; + struct addrinfo *remote_list; /* complete remote list */ + struct addrinfo *current_remote; /* remote used in the + * current connection attempt */ + struct link_socket_actual actual; /* reply to this address */ +}; + +struct link_socket_info +{ + struct link_socket_addr *lsa; + bool connection_established; + const char *ipchange_command; + const struct plugin_list *plugins; + bool remote_float; + int proto; /* Protocol (PROTO_x defined below) */ + sa_family_t af; /* Address family like AF_INET, AF_INET6 or AF_UNSPEC*/ + bool bind_ipv6_only; + int mtu_changed; /* Set to true when mtu value is changed */ +}; + +/* + * Used to extract packets encapsulated in streams into a buffer, + * in this case IP packets embedded in a TCP stream. + */ +struct stream_buf +{ + struct buffer buf_init; + struct buffer residual; + int maxlen; + bool residual_fully_formed; + + struct buffer buf; + struct buffer next; + int len; /* -1 if not yet known */ + + bool error; /* if true, fatal TCP error has occurred, + * requiring that connection be restarted */ +#if PORT_SHARE +#define PS_DISABLED 0 +#define PS_ENABLED 1 +#define PS_FOREIGN 2 + int port_share_state; +#endif +}; + +/* + * Used to set socket buffer sizes + */ +struct socket_buffer_size +{ + int rcvbuf; + int sndbuf; +}; + +/* + * This is the main socket structure used by OpenVPN. The SOCKET_ + * defines try to abstract away our implementation differences between + * using sockets on Posix vs. Win32. + */ +struct link_socket +{ + struct link_socket_info info; + + socket_descriptor_t sd; + socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */ + bool dco_installed; + +#ifdef _WIN32 + struct overlapped_io reads; + struct overlapped_io writes; + struct rw_handle rw_handle; + struct rw_handle listen_handle; /* For listening on TCP socket in server mode */ +#endif + + /* used for printing status info only */ + unsigned int rwflags_debug; + + /* used for long-term queueing of pre-accepted socket listen */ + bool listen_persistent_queued; + + const char *remote_host; + const char *remote_port; + const char *local_host; + const char *local_port; + struct cached_dns_entry *dns_cache; + bool bind_local; + +#define LS_MODE_DEFAULT 0 +#define LS_MODE_TCP_LISTEN 1 +#define LS_MODE_TCP_ACCEPT_FROM 2 + int mode; + + int resolve_retry_seconds; + int mtu_discover_type; + + struct socket_buffer_size socket_buffer_sizes; + + int mtu; /* OS discovered MTU, or 0 if unknown */ + +#define SF_USE_IP_PKTINFO (1<<0) +#define SF_TCP_NODELAY (1<<1) +#define SF_PORT_SHARE (1<<2) +#define SF_HOST_RANDOMIZE (1<<3) +#define SF_GETADDRINFO_DGRAM (1<<4) +#define SF_TCP_SPLITRESET (1<<5) +#define SF_UDP_STUFFING (1<<6) + unsigned int sockflags; + int mark; + const char *bind_dev; + + /* for stream sockets */ + struct stream_buf stream_buf; + struct buffer stream_buf_data; + bool stream_reset; + + /* HTTP proxy */ + struct http_proxy_info *http_proxy; + + /* Socks proxy */ + struct socks_proxy_info *socks_proxy; + struct link_socket_actual socks_relay; /* Socks UDP relay address */ + + /* The OpenVPN server we will use the proxy to connect to */ + const char *proxy_dest_host; + const char *proxy_dest_port; + + /* Pointer to the server-poll to trigger the timeout in function which have + * their own loop instead of using the main oop */ + struct event_timeout *server_poll_timeout; + +#if PASSTOS_CAPABILITY + /* used to get/set TOS. */ +#if defined(TARGET_LINUX) + uint8_t ptos; +#else /* all the BSDs, Solaris, MacOS use plain "int" -> see "man ip" there */ + int ptos; +#endif + bool ptos_defined; +#endif + +#ifdef ENABLE_DEBUG + int gremlin; /* --gremlin bits */ +#endif +}; + +/* + * Some Posix/Win32 differences. + */ + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#ifdef _WIN32 + +#define openvpn_close_socket(s) closesocket(s) + +int socket_recv_queue(struct link_socket *sock, int maxsize); + +int socket_send_queue(struct link_socket *sock, + struct buffer *buf, + const struct link_socket_actual *to); + +typedef struct { + union { + SOCKET s; + HANDLE h; + }; + bool is_handle; +} sockethandle_t; + +int sockethandle_finalize(sockethandle_t sh, + struct overlapped_io *io, + struct buffer *buf, + struct link_socket_actual *from); + +static inline BOOL +SocketHandleGetOverlappedResult(sockethandle_t sh, struct overlapped_io *io) +{ + return sh.is_handle ? + GetOverlappedResult(sh.h, &io->overlapped, &io->size, FALSE) : + WSAGetOverlappedResult(sh.s, &io->overlapped, &io->size, FALSE, &io->flags); +} + +static inline int +SocketHandleGetLastError(sockethandle_t sh) +{ + return sh.is_handle ? (int)GetLastError() : WSAGetLastError(); +} + +inline static void +SocketHandleSetLastError(sockethandle_t sh, DWORD err) +{ + sh.is_handle ? SetLastError(err) : WSASetLastError(err); +} + +static inline void +SocketHandleSetInvalError(sockethandle_t sh) +{ + sh.is_handle ? SetLastError(ERROR_INVALID_FUNCTION) : WSASetLastError(WSAEINVAL); +} + +#else /* ifdef _WIN32 */ + +#define openvpn_close_socket(s) close(s) + +#endif /* ifdef _WIN32 */ + +struct link_socket *link_socket_new(void); + +void socket_bind(socket_descriptor_t sd, + struct addrinfo *local, + int af_family, + const char *prefix, + bool ipv6only); + +int openvpn_connect(socket_descriptor_t sd, + const struct sockaddr *remote, + int connect_timeout, + volatile int *signal_received); + + + +/* + * Initialize link_socket object. + */ +void link_socket_init_phase1(struct context *c, int mode); + +void link_socket_init_phase2(struct context *c); + +void do_preresolve(struct context *c); + +void link_socket_close(struct link_socket *sock); + +void sd_close(socket_descriptor_t *sd); + +#define PS_SHOW_PORT_IF_DEFINED (1<<0) +#define PS_SHOW_PORT (1<<1) +#define PS_SHOW_PKTINFO (1<<2) +#define PS_DONT_SHOW_ADDR (1<<3) +#define PS_DONT_SHOW_FAMILY (1<<4) + +const char *print_sockaddr_ex(const struct sockaddr *addr, + const char *separator, + const unsigned int flags, + struct gc_arena *gc); + +static inline +const char * +print_openvpn_sockaddr_ex(const struct openvpn_sockaddr *addr, + const char *separator, + const unsigned int flags, + struct gc_arena *gc) +{ + return print_sockaddr_ex(&addr->addr.sa, separator, flags, gc); +} + +static inline +const char * +print_openvpn_sockaddr(const struct openvpn_sockaddr *addr, + struct gc_arena *gc) +{ + return print_sockaddr_ex(&addr->addr.sa, ":", PS_SHOW_PORT, gc); +} + +static inline +const char * +print_sockaddr(const struct sockaddr *addr, + struct gc_arena *gc) +{ + return print_sockaddr_ex(addr, ":", PS_SHOW_PORT, gc); +} + + + +const char *print_link_socket_actual_ex(const struct link_socket_actual *act, + const char *separator, + const unsigned int flags, + struct gc_arena *gc); + +const char *print_link_socket_actual(const struct link_socket_actual *act, + struct gc_arena *gc); + + +#define IA_EMPTY_IF_UNDEF (1<<0) +#define IA_NET_ORDER (1<<1) +const char *print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc); + +const char *print_in6_addr(struct in6_addr addr6, unsigned int flags, struct gc_arena *gc); + +const char *print_in_port_t(in_port_t port, struct gc_arena *gc); + +struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add ); + +#define SA_IP_PORT (1<<0) +#define SA_SET_IF_NONZERO (1<<1) +void setenv_sockaddr(struct env_set *es, + const char *name_prefix, + const struct openvpn_sockaddr *addr, + const unsigned int flags); + +void setenv_in_addr_t(struct env_set *es, + const char *name_prefix, + in_addr_t addr, + const unsigned int flags); + +void setenv_in6_addr(struct env_set *es, + const char *name_prefix, + const struct in6_addr *addr, + const unsigned int flags); + +void setenv_link_socket_actual(struct env_set *es, + const char *name_prefix, + const struct link_socket_actual *act, + const unsigned int flags); + +void bad_address_length(int actual, int expected); + +/* IPV4_INVALID_ADDR: returned by link_socket_current_remote() + * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints + */ +#define IPV4_INVALID_ADDR 0xffffffff +in_addr_t link_socket_current_remote(const struct link_socket_info *info); + +const struct in6_addr *link_socket_current_remote_ipv6 + (const struct link_socket_info *info); + +void link_socket_connection_initiated(struct link_socket_info *info, + const struct link_socket_actual *addr, + const char *common_name, + struct env_set *es); + +void link_socket_bad_incoming_addr(struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr); + +void set_actual_address(struct link_socket_actual *actual, + struct addrinfo *ai); + +void link_socket_bad_outgoing_addr(void); + +void setenv_trusted(struct env_set *es, const struct link_socket_info *info); + +bool link_socket_update_flags(struct link_socket *ls, unsigned int sockflags); + +void link_socket_update_buffer_sizes(struct link_socket *ls, int rcvbuf, int sndbuf); + +/* + * Low-level functions + */ + +/* return values of openvpn_inet_aton */ +#define OIA_HOSTNAME 0 +#define OIA_IP 1 +#define OIA_ERROR -1 +int openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr); + +/* integrity validation on pulled options */ +bool ip_addr_dotted_quad_safe(const char *dotted_quad); + +bool ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn); + +bool mac_addr_safe(const char *mac_addr); + +bool ipv6_addr_safe(const char *ipv6_text_addr); + +socket_descriptor_t create_socket_tcp(struct addrinfo *); + +socket_descriptor_t socket_do_accept(socket_descriptor_t sd, + struct link_socket_actual *act, + const bool nowait); + +#if UNIX_SOCK_SUPPORT + +socket_descriptor_t create_socket_unix(void); + +void socket_bind_unix(socket_descriptor_t sd, + struct sockaddr_un *local, + const char *prefix); + +socket_descriptor_t socket_accept_unix(socket_descriptor_t sd, + struct sockaddr_un *remote); + +int socket_connect_unix(socket_descriptor_t sd, + struct sockaddr_un *remote); + +void sockaddr_unix_init(struct sockaddr_un *local, const char *path); + +const char *sockaddr_unix_name(const struct sockaddr_un *local, const char *null); + +void socket_delete_unix(const struct sockaddr_un *local); + +bool unix_socket_get_peer_uid_gid(const socket_descriptor_t sd, int *uid, int *gid); + +#endif /* if UNIX_SOCK_SUPPORT */ + +/* + * DNS resolution + */ + +#define GETADDR_RESOLVE (1<<0) +#define GETADDR_FATAL (1<<1) +#define GETADDR_HOST_ORDER (1<<2) +#define GETADDR_MENTION_RESOLVE_RETRY (1<<3) +#define GETADDR_FATAL_ON_SIGNAL (1<<4) +#define GETADDR_WARN_ON_SIGNAL (1<<5) +#define GETADDR_MSG_VIRT_OUT (1<<6) +#define GETADDR_TRY_ONCE (1<<7) +#define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8) +#define GETADDR_RANDOMIZE (1<<9) +#define GETADDR_PASSIVE (1<<10) +#define GETADDR_DATAGRAM (1<<11) + +#define GETADDR_CACHE_MASK (GETADDR_DATAGRAM|GETADDR_PASSIVE) + +/** + * Translate an IPv4 addr or hostname from string form to in_addr_t + * + * In case of resolve error, it will try again for + * resolve_retry_seconds seconds. + */ +in_addr_t getaddr(unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + struct signal_info *sig_info); + +/** + * Translate an IPv6 addr or hostname from string form to in6_addr + */ +bool get_ipv6_addr(const char *hostname, struct in6_addr *network, + unsigned int *netbits, int msglevel); + +int openvpn_getaddrinfo(unsigned int flags, + const char *hostname, + const char *servname, + int resolve_retry_seconds, + struct signal_info *sig_info, + int ai_family, + struct addrinfo **res); + +/* + * Transport protocol naming and other details. + */ + +/* + * Use enum's instead of #define to allow for easier + * optional proto support + */ +enum proto_num { + PROTO_NONE, /* catch for uninitialized */ + PROTO_UDP, + PROTO_TCP, + PROTO_TCP_SERVER, + PROTO_TCP_CLIENT, + PROTO_N +}; + +static inline bool +proto_is_net(int proto) +{ + ASSERT(proto >= 0 && proto < PROTO_N); + return proto != PROTO_NONE; +} + +/** + * @brief Returns if the protocol being used is UDP + */ +static inline bool +proto_is_udp(int proto) +{ + ASSERT(proto >= 0 && proto < PROTO_N); + return proto == PROTO_UDP; +} + +/** + * @brief Return if the protocol is datagram (UDP) + * + */ +static inline bool +proto_is_dgram(int proto) +{ + return proto_is_udp(proto); +} + +/** + * @brief returns if the proto is a TCP variant (tcp-server, tcp-client or tcp) + */ +static inline bool +proto_is_tcp(int proto) +{ + ASSERT(proto >= 0 && proto < PROTO_N); + return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER; +} + + +int ascii2proto(const char *proto_name); + +sa_family_t ascii2af(const char *proto_name); + +const char *proto2ascii(int proto, sa_family_t af, bool display_form); + +const char *proto2ascii_all(struct gc_arena *gc); + +const char *proto_remote(int proto, bool remote); + +const char *addr_family_name(int af); + +/* + * Overhead added to packets by various protocols. + */ +static inline int +datagram_overhead(sa_family_t af, int proto) +{ + int overhead = 0; + overhead += (proto == PROTO_UDP) ? 8 : 20; + overhead += (af == AF_INET) ? 20 : 40; + return overhead; +} + +/* + * Misc inline functions + */ + +static inline bool +link_socket_proto_connection_oriented(int proto) +{ + return !proto_is_dgram(proto); +} + +static inline bool +link_socket_connection_oriented(const struct link_socket *sock) +{ + if (sock) + { + return link_socket_proto_connection_oriented(sock->info.proto); + } + else + { + return false; + } +} + +static inline bool +addr_defined(const struct openvpn_sockaddr *addr) +{ + if (!addr) + { + return 0; + } + switch (addr->addr.sa.sa_family) + { + case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0; + + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr); + + default: return 0; + } +} + +static inline bool +addr_local(const struct sockaddr *addr) +{ + if (!addr) + { + return false; + } + switch (addr->sa_family) + { + case AF_INET: + return ((const struct sockaddr_in *)addr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK); + + case AF_INET6: + return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)addr)->sin6_addr); + + default: + return false; + } +} + + +static inline bool +addr_defined_ipi(const struct link_socket_actual *lsa) +{ +#if ENABLE_IP_PKTINFO + if (!lsa) + { + return 0; + } + switch (lsa->dest.addr.sa.sa_family) + { +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0; + +#elif defined(IP_RECVDSTADDR) + case AF_INET: return lsa->pi.in4.s_addr != 0; + +#endif + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr); + + default: return 0; + } +#else /* if ENABLE_IP_PKTINFO */ + ASSERT(0); +#endif + return false; +} + +static inline bool +link_socket_actual_defined(const struct link_socket_actual *act) +{ + return act && addr_defined(&act->dest); +} + +static inline bool +addr_match(const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) +{ + switch (a1->addr.sa.sa_family) + { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr; + + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr); + } + ASSERT(0); + return false; +} + +static inline bool +addrlist_match(const struct openvpn_sockaddr *a1, const struct addrinfo *addrlist) +{ + const struct addrinfo *curele; + for (curele = addrlist; curele; curele = curele->ai_next) + { + switch (a1->addr.sa.sa_family) + { + case AF_INET: + if (a1->addr.in4.sin_addr.s_addr == ((struct sockaddr_in *)curele->ai_addr)->sin_addr.s_addr) + { + return true; + } + break; + + case AF_INET6: + if (IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &((struct sockaddr_in6 *) curele->ai_addr)->sin6_addr)) + { + return true; + } + break; + + default: + ASSERT(0); + } + } + return false; +} + +static inline in_addr_t +addr_host(const struct openvpn_sockaddr *addr) +{ + /* + * "public" addr returned is checked against ifconfig for + * possible clash: non sense for now given + * that we do ifconfig only IPv4 + */ + if (addr->addr.sa.sa_family != AF_INET) + { + return 0; + } + return ntohl(addr->addr.in4.sin_addr.s_addr); +} + + +static inline bool +addrlist_port_match(const struct openvpn_sockaddr *a1, const struct addrinfo *a2) +{ + const struct addrinfo *curele; + for (curele = a2; curele; curele = curele->ai_next) + { + switch (a1->addr.sa.sa_family) + { + case AF_INET: + if (curele->ai_family == AF_INET + && a1->addr.in4.sin_addr.s_addr == ((struct sockaddr_in *)curele->ai_addr)->sin_addr.s_addr + && a1->addr.in4.sin_port == ((struct sockaddr_in *)curele->ai_addr)->sin_port) + { + return true; + } + break; + + case AF_INET6: + if (curele->ai_family == AF_INET6 + && IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &((struct sockaddr_in6 *) curele->ai_addr)->sin6_addr) + && a1->addr.in6.sin6_port == ((struct sockaddr_in6 *) curele->ai_addr)->sin6_port) + { + return true; + } + break; + + default: + ASSERT(0); + } + } + return false; +} + + + +static inline bool +addr_port_match(const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) +{ + switch (a1->addr.sa.sa_family) + { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr + && a1->addr.in4.sin_port == a2->addr.in4.sin_port; + + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr) + && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port; + } + ASSERT(0); + return false; +} + +static inline bool +addr_match_proto(const struct openvpn_sockaddr *a1, + const struct openvpn_sockaddr *a2, + const int proto) +{ + return link_socket_proto_connection_oriented(proto) + ? addr_match(a1, a2) + : addr_port_match(a1, a2); +} + + +static inline bool +addrlist_match_proto(const struct openvpn_sockaddr *a1, + struct addrinfo *addr_list, + const int proto) +{ + return link_socket_proto_connection_oriented(proto) + ? addrlist_match(a1, addr_list) + : addrlist_port_match(a1, addr_list); +} + +static inline void +addr_zero_host(struct openvpn_sockaddr *addr) +{ + switch (addr->addr.sa.sa_family) + { + case AF_INET: + addr->addr.in4.sin_addr.s_addr = 0; + break; + + case AF_INET6: + memset(&addr->addr.in6.sin6_addr, 0, sizeof(struct in6_addr)); + break; + } +} + +static inline int +af_addr_size(sa_family_t af) +{ + switch (af) + { + case AF_INET: return sizeof(struct sockaddr_in); + + case AF_INET6: return sizeof(struct sockaddr_in6); + + default: +#if 0 + /* could be called from socket_do_accept() with empty addr */ + msg(M_ERR, "Bad address family: %d\n", af); + ASSERT(0); +#endif + return 0; + } +} + +static inline bool +link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2) +{ + return addr_port_match(&a1->dest, &a2->dest); +} + +#if PORT_SHARE + +static inline bool +socket_foreign_protocol_detected(const struct link_socket *sock) +{ + return link_socket_connection_oriented(sock) + && sock->stream_buf.port_share_state == PS_FOREIGN; +} + +static inline const struct buffer * +socket_foreign_protocol_head(const struct link_socket *sock) +{ + return &sock->stream_buf.buf; +} + +static inline int +socket_foreign_protocol_sd(const struct link_socket *sock) +{ + return sock->sd; +} + +#endif /* if PORT_SHARE */ + +static inline bool +socket_connection_reset(const struct link_socket *sock, int status) +{ + if (link_socket_connection_oriented(sock)) + { + if (sock->stream_reset || sock->stream_buf.error) + { + return true; + } + else if (status < 0) + { + const int err = openvpn_errno(); +#ifdef _WIN32 + return err == WSAECONNRESET || err == WSAECONNABORTED + || err == ERROR_CONNECTION_ABORTED; +#else + return err == ECONNRESET; +#endif + } + } + return false; +} + +static inline bool +link_socket_verify_incoming_addr(struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr) +{ + if (buf->len > 0) + { + switch (from_addr->dest.addr.sa.sa_family) + { + case AF_INET6: + case AF_INET: + if (!link_socket_actual_defined(from_addr)) + { + return false; + } + if (info->remote_float || (!info->lsa->remote_list)) + { + return true; + } + if (addrlist_match_proto(&from_addr->dest, info->lsa->remote_list, info->proto)) + { + return true; + } + } + } + return false; +} + +static inline void +link_socket_get_outgoing_addr(struct buffer *buf, + const struct link_socket_info *info, + struct link_socket_actual **act) +{ + if (buf->len > 0) + { + struct link_socket_addr *lsa = info->lsa; + if (link_socket_actual_defined(&lsa->actual)) + { + *act = &lsa->actual; + } + else + { + link_socket_bad_outgoing_addr(); + buf->len = 0; + *act = NULL; + } + } +} + +static inline void +link_socket_set_outgoing_addr(struct link_socket_info *info, + const struct link_socket_actual *act, + const char *common_name, + struct env_set *es) +{ + struct link_socket_addr *lsa = info->lsa; + if ( + /* new or changed address? */ + (!info->connection_established + || !addr_match_proto(&act->dest, &lsa->actual.dest, info->proto) + ) + && + /* address undef or address == remote or --float */ + (info->remote_float + || (!lsa->remote_list || addrlist_match_proto(&act->dest, lsa->remote_list, info->proto)) + ) + ) + { + link_socket_connection_initiated(info, act, common_name, es); + } +} + +bool stream_buf_read_setup_dowork(struct link_socket *sock); + +static inline bool +stream_buf_read_setup(struct link_socket *sock) +{ + if (link_socket_connection_oriented(sock)) + { + return stream_buf_read_setup_dowork(sock); + } + else + { + return true; + } +} + +/* + * Socket Read Routines + */ + +int link_socket_read_tcp(struct link_socket *sock, + struct buffer *buf); + +#ifdef _WIN32 + +static inline int +link_socket_read_udp_win32(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from) +{ + sockethandle_t sh = { .s = sock->sd }; + if (sock->dco_installed) + { + *from = sock->info.lsa->actual; + sh.is_handle = true; + } + return sockethandle_finalize(sh, &sock->reads, buf, from); +} + +#else /* ifdef _WIN32 */ + +int link_socket_read_udp_posix(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from); + +#endif /* ifdef _WIN32 */ + +/* read a TCP or UDP packet from link */ +static inline int +link_socket_read(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from) +{ +#ifdef _WIN32 + if (proto_is_udp(sock->info.proto) || sock->dco_installed) +#else + if (proto_is_udp(sock->info.proto)) +#endif + /* unified UDPv4 and UDPv6, for DCO the kernel + * will strip the length header */ + { + int res; + +#ifdef _WIN32 + res = link_socket_read_udp_win32(sock, buf, from); +#else + res = link_socket_read_udp_posix(sock, buf, from); +#endif + return res; + } + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + /* from address was returned by accept */ + from->dest = sock->info.lsa->actual.dest; + return link_socket_read_tcp(sock, buf); + } + else + { + ASSERT(0); + return -1; /* NOTREACHED */ + } +} + +/* + * Socket Write routines + */ + +int link_socket_write_tcp(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to); + +#ifdef _WIN32 + +static inline int +link_socket_write_win32(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + int err = 0; + int status = 0; + sockethandle_t sh = { .s = sock->sd, .is_handle = sock->dco_installed }; + if (overlapped_io_active(&sock->writes)) + { + status = sockethandle_finalize(sh, &sock->writes, NULL, NULL); + if (status < 0) + { + err = SocketHandleGetLastError(sh); + } + } + socket_send_queue(sock, buf, to); + if (status < 0) + { + SocketHandleSetLastError(sh, err); + return status; + } + else + { + return BLEN(buf); + } +} + +#else /* ifdef _WIN32 */ + +size_t link_socket_write_udp_posix_sendmsg(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to); + + +static inline size_t +link_socket_write_udp_posix(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ +#if ENABLE_IP_PKTINFO + if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO) + && addr_defined_ipi(to)) + { + return link_socket_write_udp_posix_sendmsg(sock, buf, to); + } + else +#endif + return sendto(sock->sd, BPTR(buf), BLEN(buf), 0, + (struct sockaddr *) &to->dest.addr.sa, + (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); +} + +static inline size_t +link_socket_write_tcp_posix(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + return send(sock->sd, BPTR(buf), BLEN(buf), MSG_NOSIGNAL); +} + +#endif /* ifdef _WIN32 */ + +static inline size_t +link_socket_write_udp(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ +#ifndef P_CONTROL_HARD_RESET_CLIENT_V2 +#define P_CONTROL_HARD_RESET_CLIENT_V2 7 +#endif +#ifndef P_CONTROL_HARD_RESET_CLIENT_V3 +#define P_CONTROL_HARD_RESET_CLIENT_V3 10 +#endif +#ifndef P_OPCODE_SHIFT +#define P_OPCODE_SHIFT 3 +#endif + int rand_bytes(uint8_t *output, int len); + +#define STUFFING_LEN_MAX 1200 + + uint8_t opcode = *BPTR(buf) >> P_OPCODE_SHIFT; + if ( + sock->sockflags & SF_UDP_STUFFING + && (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 + || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) + ) + { + uint16_t stuffing_len; + rand_bytes((uint8_t*)&stuffing_len, sizeof(stuffing_len)); + stuffing_len = (stuffing_len % (STUFFING_LEN_MAX - 10)) + 10; + + uint8_t stuffing_data[STUFFING_LEN_MAX] = {0}; + rand_bytes(stuffing_data, sizeof(stuffing_data)); + struct buffer stuffing_buf = alloc_buf(STUFFING_LEN_MAX); + buf_write(&stuffing_buf, stuffing_data, stuffing_len); + +#ifdef _WIN32 + link_socket_write_win32(sock, &stuffing_buf, to); +#else + link_socket_write_udp_posix(sock, &stuffing_buf, to); +#endif + + free_buf(&stuffing_buf); + } + + +#ifdef _WIN32 + return link_socket_write_win32(sock, buf, to); +#else + return link_socket_write_udp_posix(sock, buf, to); +#endif +} + +/* write a TCP or UDP packet to link */ +static inline int +link_socket_write(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + if (proto_is_udp(sock->info.proto) || sock->dco_installed) + { + /* unified UDPv4 and UDPv6 and DCO (kernel adds size header) */ + return link_socket_write_udp(sock, buf, to); + } + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + return link_socket_write_tcp(sock, buf, to); + } + else + { + ASSERT(0); + return -1; /* NOTREACHED */ + } +} + +#if PASSTOS_CAPABILITY + +/* + * Extract TOS bits. Assumes that ipbuf is a valid IPv4 packet. + */ +static inline void +link_socket_extract_tos(struct link_socket *ls, const struct buffer *ipbuf) +{ + if (ls && ipbuf) + { + struct openvpn_iphdr *iph = (struct openvpn_iphdr *) BPTR(ipbuf); + ls->ptos = iph->tos; + ls->ptos_defined = true; + } +} + +/* + * Set socket properties to reflect TOS bits which were extracted + * from tunnel packet. + */ +static inline void +link_socket_set_tos(struct link_socket *ls) +{ + if (ls && ls->ptos_defined) + { + setsockopt(ls->sd, IPPROTO_IP, IP_TOS, (const void *)&ls->ptos, sizeof(ls->ptos)); + } +} + +#endif /* if PASSTOS_CAPABILITY */ + +/* + * Socket I/O wait functions + */ + +static inline bool +socket_read_residual(const struct link_socket *s) +{ + return s && s->stream_buf.residual_fully_formed; +} + +static inline event_t +socket_event_handle(const struct link_socket *s) +{ +#ifdef _WIN32 + return &s->rw_handle; +#else + return s->sd; +#endif +} + +event_t socket_listen_event_handle(struct link_socket *s); + +unsigned int +socket_set(struct link_socket *s, + struct event_set *es, + unsigned int rwflags, + void *arg, + unsigned int *persistent); + +static inline void +socket_set_listen_persistent(struct link_socket *s, + struct event_set *es, + void *arg) +{ + if (s && !s->listen_persistent_queued) + { + event_ctl(es, socket_listen_event_handle(s), EVENT_READ, arg); + s->listen_persistent_queued = true; + } +} + +static inline void +socket_reset_listen_persistent(struct link_socket *s) +{ +#ifdef _WIN32 + reset_net_event_win32(&s->listen_handle, s->sd); +#endif +} + +const char *socket_stat(const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc); + +#endif /* SOCKET_H */ diff --git a/tools/deps/custom_stunnel/patches/0001-vc.mak.patch b/tools/deps/custom_stunnel/patches/0001-vc.mak.patch new file mode 100644 index 000000000..91668f08b --- /dev/null +++ b/tools/deps/custom_stunnel/patches/0001-vc.mak.patch @@ -0,0 +1,129 @@ +From c5f65c439b3eb1e7ceefd051bcfe68fb8591fb55 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Jun 2023 02:45:48 +0300 +Subject: [PATCH 1/3] vc.mak + +--- + src/vc.mak | 62 ++++++++++++++++++++++++++---------------------------- + 1 file changed, 30 insertions(+), 32 deletions(-) + +diff --git a/src/vc.mak b/src/vc.mak +index 500135d..bdc344e 100644 +--- a/src/vc.mak ++++ b/src/vc.mak +@@ -1,4 +1,4 @@ +-# vc.mak by Michal Trojnara 1998-2022 ++# vc.mak by Michal Trojnara 1998-2021 + # with help of David Gillingham + # with help of Pierre Delaage + +@@ -11,10 +11,8 @@ + !IF [ml64.exe /help >NUL 2>&1] + TARGET=win32 + SSLLIBS=libcrypto.lib libssl.lib +-# or change libraries for OpenSSL older than 1.1.0 +-#SSLLIBS=libeay32.lib ssleay32.lib + !ELSE +-TARGET=win64 ++TARGET=$(TARGET_ARCH) + SSLLIBS=libcrypto.lib libssl.lib + !ENDIF + !MESSAGE Detected target: $(TARGET) +@@ -25,9 +23,9 @@ SSLLIBS=libcrypto.lib libssl.lib + # http://www.slproweb.com/products/Win32OpenSSL.html + #SSLDIR=C:\OpenSSL-Win32 + # or compile one yourself +-SSLDIR=\devel\$(TARGET)\openssl ++#SSLDIR=\devel\$(TARGET)\openssl + # or simply install with "nmake -f ms\ntdll.mak install" +-#SSLDIR=\usr\local\ssl ++SSLDIR=$(OPENSSL_HOME) + + INCDIR=$(SSLDIR)\include + LIBDIR=$(SSLDIR)\lib +@@ -39,11 +37,11 @@ BINROOT=..\bin + BIN=$(BINROOT)\$(TARGET) + + SHAREDOBJS=$(OBJ)\stunnel.obj $(OBJ)\ssl.obj $(OBJ)\ctx.obj \ +- $(OBJ)\verify.obj $(OBJ)\file.obj $(OBJ)\client.obj \ +- $(OBJ)\protocol.obj $(OBJ)\sthreads.obj $(OBJ)\log.obj \ +- $(OBJ)\options.obj $(OBJ)\network.obj $(OBJ)\resolver.obj \ +- $(OBJ)\str.obj $(OBJ)\tls.obj $(OBJ)\fd.obj $(OBJ)\dhparam.obj \ +- $(OBJ)\cron.obj ++ $(OBJ)\verify.obj $(OBJ)\file.obj $(OBJ)\client.obj \ ++ $(OBJ)\protocol.obj $(OBJ)\sthreads.obj $(OBJ)\log.obj \ ++ $(OBJ)\options.obj $(OBJ)\network.obj $(OBJ)\resolver.obj \ ++ $(OBJ)\str.obj $(OBJ)\tls.obj $(OBJ)\fd.obj $(OBJ)\dhparam.obj \ ++ $(OBJ)\cron.obj + GUIOBJS=$(OBJ)\ui_win_gui.obj $(OBJ)\resources.res + CLIOBJS=$(OBJ)\ui_win_cli.obj + +@@ -61,45 +59,45 @@ CLILIBS= + # /LIBPATH:"$(LIBDIR)\VC\static" libeay32MD.lib ssleay32MD.lib + + {$(SRC)\}.c{$(OBJ)\}.obj: +- $(CC) $(CFLAGS) -Fo$@ -c $< ++ $(CC) $(CFLAGS) -Fo$@ -c $< + + {$(SRC)\}.rc{$(OBJ)\}.res: +- $(RC) -fo$@ -r $< ++ $(RC) -fo$@ -r $< + + all: build + + build: makedirs $(BIN)\stunnel.exe $(BIN)\tstunnel.exe + + clean: +- -@ del $(SHAREDOBJS) >NUL 2>&1 +- -@ del $(GUIOBJS) >NUL 2>&1 +- -@ del $(CLIOBJS) >NUL 2>&1 ++ -@ del $(SHAREDOBJS) >NUL 2>&1 ++ -@ del $(GUIOBJS) >NUL 2>&1 ++ -@ del $(CLIOBJS) >NUL 2>&1 + # -@ del *.manifest >NUL 2>&1 +- -@ del $(BIN)\stunnel.exe >NUL 2>&1 +- -@ del $(BIN)\stunnel.exe.manifest >NUL 2>&1 +- -@ del $(BIN)\tstunnel.exe >NUL 2>&1 +- -@ del $(BIN)\tstunnel.exe.manifest >NUL 2>&1 +- -@ rmdir $(OBJ) >NUL 2>&1 +- -@ rmdir $(BIN) >NUL 2>&1 ++ -@ del $(BIN)\stunnel.exe >NUL 2>&1 ++ -@ del $(BIN)\stunnel.exe.manifest >NUL 2>&1 ++ -@ del $(BIN)\tstunnel.exe >NUL 2>&1 ++ -@ del $(BIN)\tstunnel.exe.manifest >NUL 2>&1 ++ -@ rmdir $(OBJ) >NUL 2>&1 ++ -@ rmdir $(BIN) >NUL 2>&1 + + makedirs: +- -@ IF NOT EXIST $(OBJROOT) mkdir $(OBJROOT) >NUL 2>&1 +- -@ IF NOT EXIST $(OBJ) mkdir $(OBJ) >NUL 2>&1 +- -@ IF NOT EXIST $(BINROOT) mkdir $(BINROOT) >NUL 2>&1 +- -@ IF NOT EXIST $(BIN) mkdir $(BIN) >NUL 2>&1 ++ -@ IF NOT EXIST $(OBJROOT) mkdir $(OBJROOT) >NUL 2>&1 ++ -@ IF NOT EXIST $(OBJ) mkdir $(OBJ) >NUL 2>&1 ++ -@ IF NOT EXIST $(BINROOT) mkdir $(BINROOT) >NUL 2>&1 ++ -@ IF NOT EXIST $(BIN) mkdir $(BIN) >NUL 2>&1 + + $(SHAREDOBJS): *.h vc.mak + $(GUIOBJS): *.h vc.mak + $(CLIOBJS): *.h vc.mak + + $(BIN)\stunnel.exe: $(SHAREDOBJS) $(GUIOBJS) +- $(LINK) $(LDFLAGS) $(SHAREDLIBS) $(GUILIBS) /LIBPATH:"$(LIBDIR)" $(SSLLIBS) /OUT:$@ $** +- IF EXIST $@.manifest \ +- mt -nologo -manifest $@.manifest -outputresource:$@;1 ++ $(LINK) $(LDFLAGS) $(SHAREDLIBS) $(GUILIBS) /LIBPATH:"$(LIBDIR)" $(SSLLIBS) /OUT:$@ $** ++ IF EXIST $@.manifest \ ++ mt -nologo -manifest $@.manifest -outputresource:$@;1 + + $(BIN)\tstunnel.exe: $(SHAREDOBJS) $(CLIOBJS) +- $(LINK) $(LDFLAGS) $(SHAREDLIBS) $(CLILIBS) /LIBPATH:"$(LIBDIR)" $(SSLLIBS) /OUT:$@ $** +- IF EXIST $@.manifest \ +- mt -nologo -manifest $@.manifest -outputresource:$@;1 ++ $(LINK) $(LDFLAGS) $(SHAREDLIBS) $(CLILIBS) /LIBPATH:"$(LIBDIR)" $(SSLLIBS) /OUT:$@ $** ++ IF EXIST $@.manifest \ ++ mt -nologo -manifest $@.manifest -outputresource:$@;1 + + # end of vc.mak +-- +2.40.1.windows.1 + diff --git a/tools/deps/custom_stunnel/patches/0002-Fix-SSL-option-variable-size-on-Windows.patch b/tools/deps/custom_stunnel/patches/0002-Fix-SSL-option-variable-size-on-Windows.patch new file mode 100644 index 000000000..7f9f92f89 --- /dev/null +++ b/tools/deps/custom_stunnel/patches/0002-Fix-SSL-option-variable-size-on-Windows.patch @@ -0,0 +1,74 @@ +From b52603dd193bdd29d0761b52be641a60a90e83ea Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Jun 2023 02:47:05 +0300 +Subject: [PATCH 2/3] Fix SSL option variable size on Windows + +--- + src/ctx.c | 2 +- + src/options.c | 10 +++++----- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/ctx.c b/src/ctx.c +index cc0806c..32e633d 100644 +--- a/src/ctx.c ++++ b/src/ctx.c +@@ -134,7 +134,7 @@ NOEXPORT void sslerror_log(unsigned long, const char *, int, const char *); + /**************************************** initialize section->ctx */ + + #if OPENSSL_VERSION_NUMBER>=0x10100000L +-typedef long unsigned SSL_OPTIONS_TYPE; ++typedef uint64_t SSL_OPTIONS_TYPE; + #else + typedef long SSL_OPTIONS_TYPE; + #endif +diff --git a/src/options.c b/src/options.c +index 9ac9c7e..526ddb3 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -103,7 +103,7 @@ NOEXPORT void key_free(TICKET_KEY *); + + typedef struct { + const char *name; +- long unsigned value; ++ uint64_t value; + } SSL_OPTION; + + static const SSL_OPTION ssl_opts[] = { +@@ -269,7 +269,7 @@ static const SSL_OPTION ssl_opts[] = { + {NULL, 0} + }; + +-NOEXPORT long unsigned parse_ssl_option(char *); ++NOEXPORT uint64_t parse_ssl_option(char *); + NOEXPORT void print_ssl_options(void); + + NOEXPORT SOCK_OPT *socket_options_init(void); +@@ -2427,7 +2427,7 @@ NOEXPORT const char *parse_service_option(CMD cmd, SERVICE_OPTIONS **section_ptr + break; + #if OPENSSL_VERSION_NUMBER>=0x009080dfL + if(*arg=='-') { +- long unsigned tmp=parse_ssl_option(arg+1); ++ uint64_t tmp=parse_ssl_option(arg+1); + if(tmp==INVALID_SSL_OPTION) + return "Illegal TLS option"; + section->ssl_options_clear|=tmp; +@@ -2435,7 +2435,7 @@ NOEXPORT const char *parse_service_option(CMD cmd, SERVICE_OPTIONS **section_ptr + } + #endif /* OpenSSL 0.9.8m or later */ + { +- long unsigned tmp=parse_ssl_option(arg); ++ uint64_t tmp=parse_ssl_option(arg); + if(tmp==INVALID_SSL_OPTION) + return "Illegal TLS option"; + section->ssl_options_set|=tmp; +@@ -4100,7 +4100,7 @@ NOEXPORT const char *parse_debug_level(char *arg, SERVICE_OPTIONS *section) { + + /**************************************** TLS options */ + +-NOEXPORT long unsigned parse_ssl_option(char *arg) { ++NOEXPORT uint64_t parse_ssl_option(char *arg) { + const SSL_OPTION *option; + + for(option=ssl_opts; option->name; ++option) +-- +2.40.1.windows.1 + diff --git a/tools/deps/custom_stunnel/patches/0003-Add-support-for-TLSEXT_PADDING_SUPER.patch b/tools/deps/custom_stunnel/patches/0003-Add-support-for-TLSEXT_PADDING_SUPER.patch new file mode 100644 index 000000000..d6f283d98 --- /dev/null +++ b/tools/deps/custom_stunnel/patches/0003-Add-support-for-TLSEXT_PADDING_SUPER.patch @@ -0,0 +1,25 @@ +From 8d2e7f7d4044cd27850a5f630cddc8f20c421ab0 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Jun 2023 02:47:42 +0300 +Subject: [PATCH 3/3] Add support for TLSEXT_PADDING_SUPER + +--- + src/options.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/options.c b/src/options.c +index 526ddb3..9888588 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -263,6 +263,9 @@ static const SSL_OPTION ssl_opts[] = { + #ifdef SSL_OP_TLSEXT_PADDING + {"TLSEXT_PADDING", SSL_OP_TLSEXT_PADDING}, + #endif ++#ifdef SSL_OP_TLSEXT_PADDING_SUPER ++ {"TLSEXT_PADDING_SUPER", SSL_OP_TLSEXT_PADDING_SUPER}, ++#endif + #ifdef SSL_OP_TLS_ROLLBACK_BUG + {"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG}, + #endif +-- +2.40.1.windows.1 + diff --git a/tools/deps/custom_stunnel/src/ctx.c b/tools/deps/custom_stunnel/src/ctx.c new file mode 100644 index 000000000..32e633d47 --- /dev/null +++ b/tools/deps/custom_stunnel/src/ctx.c @@ -0,0 +1,1717 @@ +/* + * stunnel TLS offloading and load-balancing proxy + * Copyright (C) 1998-2022 Michal Trojnara + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Linking stunnel statically or dynamically with other modules is making + * a combined work based on stunnel. Thus, the terms and conditions of + * the GNU General Public License cover the whole combination. + * + * In addition, as a special exception, the copyright holder of stunnel + * gives you permission to combine stunnel with free software programs or + * libraries that are released under the GNU LGPL and with code included + * in the standard release of OpenSSL under the OpenSSL License (or + * modified versions of such code, with unchanged license). You may copy + * and distribute such a system following the terms of the GNU GPL for + * stunnel and the licenses of the other code concerned. + * + * Note that people who make modified versions of stunnel are not obligated + * to grant this special exception for their modified versions; it is their + * choice whether to do so. The GNU General Public License gives permission + * to release a modified version without this exception; this exception + * also makes it possible to release a modified version which carries + * forward this exception. + */ + +#include "prototypes.h" + +#if defined(__GNUC__) && defined(USE_WIN32) +#pragma GCC diagnostic ignored "-Wformat" +#endif /* defined(__GNUC__) && defined(USE_WIN32) */ + +SERVICE_OPTIONS *current_section=NULL; + +/* try an empty passphrase first */ +static char cached_passwd[PEM_BUFSIZE]=""; +static int cached_len=0; + +#ifndef OPENSSL_NO_DH +DH *dh_params=NULL; +int dh_temp_params=0; +#endif /* OPENSSL_NO_DH */ + +/**************************************** prototypes */ + +/* SNI */ +#ifndef OPENSSL_NO_TLSEXT +NOEXPORT int servername_cb(SSL *, int *, void *); +NOEXPORT int matches_wildcard(const char *, const char *); +#endif + +/* DH/ECDH */ +#ifndef OPENSSL_NO_DH +NOEXPORT int dh_init(SERVICE_OPTIONS *); +NOEXPORT DH *dh_read(char *); +#endif /* OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH +NOEXPORT int ecdh_init(SERVICE_OPTIONS *); +#endif /* USE_ECDH */ + +/* configuration commands */ +NOEXPORT int conf_init(SERVICE_OPTIONS *section); + +/* authentication */ +NOEXPORT int auth_init(SERVICE_OPTIONS *); +#ifndef OPENSSL_NO_PSK +NOEXPORT unsigned psk_client_callback(SSL *, const char *, + char *, unsigned, unsigned char *, unsigned); +NOEXPORT unsigned psk_server_callback(SSL *, const char *, + unsigned char *, unsigned); +#endif /* !defined(OPENSSL_NO_PSK) */ +NOEXPORT int load_cert_file(SERVICE_OPTIONS *); +NOEXPORT int load_key_file(SERVICE_OPTIONS *); +NOEXPORT int pkcs12_extension(const char *); +NOEXPORT int load_pkcs12_file(SERVICE_OPTIONS *); +#ifndef OPENSSL_NO_ENGINE +NOEXPORT int load_cert_engine(SERVICE_OPTIONS *); +NOEXPORT int load_key_engine(SERVICE_OPTIONS *); +#endif +NOEXPORT int cache_passwd_get_cb(char *, int, int, void *); +NOEXPORT int cache_passwd_set_cb(char *, int, int, void *); +NOEXPORT void set_prompt(const char *); +NOEXPORT int ui_retry(void); + +/* session tickets */ +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +NOEXPORT int generate_session_ticket_cb(SSL *, void *); +NOEXPORT int decrypt_session_ticket_cb(SSL *, SSL_SESSION *, + const unsigned char *, size_t, SSL_TICKET_STATUS, void *); +#endif /* OpenSSL 1.1.1 or later */ + +#if OPENSSL_VERSION_NUMBER>=0x10000000L +NOEXPORT int ssl_tlsext_ticket_key_cb(SSL *, unsigned char *, + unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int); +#endif /* OpenSSL 1.0.0 or later */ + +/* session callbacks */ +NOEXPORT int sess_new_cb(SSL *, SSL_SESSION *); +NOEXPORT void new_chain(CLI *); +NOEXPORT void session_cache_save(CLI *, SSL_SESSION *); +NOEXPORT SSL_SESSION *sess_get_cb(SSL *, +#if OPENSSL_VERSION_NUMBER>=0x10100000L + const +#endif + unsigned char *, int, int *); +NOEXPORT void sess_remove_cb(SSL_CTX *, SSL_SESSION *); + +/* sessiond interface */ +NOEXPORT void cache_new(SSL *, SSL_SESSION *); +NOEXPORT SSL_SESSION *cache_get(SSL *, const unsigned char *, int); +NOEXPORT void cache_remove(SSL_CTX *, SSL_SESSION *); +NOEXPORT void cache_transfer(SSL_CTX *, const u_char, const long, + const u_char *, const size_t, + const u_char *, const size_t, + unsigned char **, size_t *); + +/* info callbacks */ +NOEXPORT void info_callback(const SSL *, int, int); + +NOEXPORT void sslerror_queue(void); +NOEXPORT void sslerror_log(unsigned long, const char *, int, const char *); + +/**************************************** initialize section->ctx */ + +#if OPENSSL_VERSION_NUMBER>=0x10100000L +typedef uint64_t SSL_OPTIONS_TYPE; +#else +typedef long SSL_OPTIONS_TYPE; +#endif + +int context_init(SERVICE_OPTIONS *section) { /* init TLS context */ + /* create a new TLS context */ +#if OPENSSL_VERSION_NUMBER>=0x10100000L +#if OPENSSL_VERSION_NUMBER>=0x30000000L + section->ctx=SSL_CTX_new_ex(NULL, + EVP_default_properties_is_fips_enabled(NULL) ? + "fips=yes" : "provider!=fips", + section->option.client ? + TLS_client_method() : TLS_server_method()); +#else /* OPENSSL_VERSION_NUMBER<0x30000000L */ + section->ctx=SSL_CTX_new(section->option.client ? + TLS_client_method() : TLS_server_method()); +#endif /* OPENSSL_VERSION_NUMBER>=0x30000000L */ + if(!SSL_CTX_set_min_proto_version(section->ctx, + section->min_proto_version)) { + s_log(LOG_ERR, "Failed to set the minimum protocol version 0x%X", + section->min_proto_version); + return 1; /* FAILED */ + } + if(!SSL_CTX_set_max_proto_version(section->ctx, + section->max_proto_version)) { + s_log(LOG_ERR, "Failed to set the maximum protocol version 0x%X", + section->max_proto_version); + return 1; /* FAILED */ + } +#else /* OPENSSL_VERSION_NUMBER<0x10100000L */ + if(section->option.client) + section->ctx=SSL_CTX_new(section->client_method); + else /* server mode */ + section->ctx=SSL_CTX_new(section->server_method); +#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */ + if(!section->ctx) { + sslerror("SSL_CTX_new"); + return 1; /* FAILED */ + } + + /* allow callbacks to access their SERVICE_OPTIONS structure */ + if(!SSL_CTX_set_ex_data(section->ctx, index_ssl_ctx_opt, section)) { + sslerror("SSL_CTX_set_ex_data"); + return 1; /* FAILED */ + } + current_section=section; /* setup current section for callbacks */ + +#if OPENSSL_VERSION_NUMBER>=0x10100000L + /* set the security level */ + if(section->security_level>=0) { + /* set the user-specified value */ + SSL_CTX_set_security_level(section->ctx, section->security_level); + s_log(LOG_INFO, "User-specified security level set: %d", + section->security_level); + } else if(SSL_CTX_get_security_level(section->ctx)ctx, DEFAULT_SECURITY_LEVEL); + s_log(LOG_INFO, "stunnel default security level set: %d", + DEFAULT_SECURITY_LEVEL); + } else { /* our default is not more secure than the OpenSSL default */ + s_log(LOG_INFO, "OpenSSL security level is used: %d", + SSL_CTX_get_security_level(section->ctx)); + } +#endif /* OpenSSL 1.1.0 or later */ + + /* ciphers */ + if(section->cipher_list) { + s_log(LOG_DEBUG, "Ciphers: %s", section->cipher_list); + if(!SSL_CTX_set_cipher_list(section->ctx, section->cipher_list)) { + sslerror("SSL_CTX_set_cipher_list"); + return 1; /* FAILED */ + } + } + +#ifndef OPENSSL_NO_TLS1_3 + /* ciphersuites */ + if(section->ciphersuites) { + s_log(LOG_DEBUG, "TLSv1.3 ciphersuites: %s", section->ciphersuites); + if(!SSL_CTX_set_ciphersuites(section->ctx, section->ciphersuites)) { + sslerror("SSL_CTX_set_ciphersuites"); + return 1; /* FAILED */ + } + } +#endif /* TLS 1.3 */ + + /* TLS options: configure the stunnel defaults first */ + SSL_CTX_set_options(section->ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + /* no session ticket gets sent to the client at all in TLSv1.2 + and below, but a stateful ticket will be sent in TLSv1.3 */ +#ifdef SSL_OP_NO_TICKET + if(!section->option.client && !section->option.session_resume) { + SSL_CTX_set_options(section->ctx, SSL_OP_NO_TICKET); + } +#endif +#ifdef SSL_OP_NO_COMPRESSION + /* we implemented a better way to disable compression if needed */ + SSL_CTX_clear_options(section->ctx, SSL_OP_NO_COMPRESSION); +#endif /* SSL_OP_NO_COMPRESSION */ + + /* TLS options: configure the user-specified values */ + SSL_CTX_set_options(section->ctx, + (SSL_OPTIONS_TYPE)(section->ssl_options_set)); +#if OPENSSL_VERSION_NUMBER>=0x009080dfL + SSL_CTX_clear_options(section->ctx, + (SSL_OPTIONS_TYPE)(section->ssl_options_clear)); +#endif /* OpenSSL 0.9.8m or later */ + + /* TLS options: log the configured values */ +#if OPENSSL_VERSION_NUMBER>=0x009080dfL + s_log(LOG_DEBUG, + "TLS options: 0x%" PRIX64 " (+0x%" PRIX64 ", -0x%" PRIX64 ")", + SSL_CTX_get_options(section->ctx), + section->ssl_options_set, section->ssl_options_clear); +#else /* OpenSSL older than 0.9.8m */ + s_log(LOG_DEBUG, "TLS options: 0x%" PRIX64 " (+0x%" PRIX64 ")", + SSL_CTX_get_options(section->ctx), section->ssl_options_set); +#endif /* OpenSSL 0.9.8m or later */ + + /* initialize OpenSSL CONF options */ + if(conf_init(section)) + return 1; /* FAILED */ + + /* setup mode of operation for the TLS state machine */ +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode(section->ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS); +#else + SSL_CTX_set_mode(section->ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + + /* setup session tickets */ +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + SSL_CTX_set_session_ticket_cb(section->ctx, generate_session_ticket_cb, + decrypt_session_ticket_cb, NULL); +#endif /* OpenSSL 1.1.1 or later */ + +#if OPENSSL_VERSION_NUMBER>=0x10000000L + if((section->ticket_key)&&(section->ticket_mac)) + SSL_CTX_set_tlsext_ticket_key_cb(section->ctx, ssl_tlsext_ticket_key_cb); +#endif /* OpenSSL 1.0.0 or later */ + + /* setup session cache */ + if(!section->option.client) { + unsigned servname_len=(unsigned)strlen(section->servname); + if(servname_len>SSL_MAX_SSL_SESSION_ID_LENGTH) + servname_len=SSL_MAX_SSL_SESSION_ID_LENGTH; +#ifndef OPENSSL_NO_TLS1_3 + /* suppress all tickets (stateful and stateless) in TLSv1.3 */ + if(!section->option.session_resume && !SSL_CTX_set_num_tickets(section->ctx, 0)) { + sslerror("SSL_CTX_set_num_tickets"); + return 1; /* FAILED */ + } +#endif /* TLS 1.3 */ + if(!SSL_CTX_set_session_id_context(section->ctx, + (unsigned char *)section->servname, servname_len)) { + sslerror("SSL_CTX_set_session_id_context"); + return 1; /* FAILED */ + } + } + if(section->option.session_resume) { + SSL_CTX_set_session_cache_mode(section->ctx, + SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL_STORE); + } else { + SSL_CTX_set_session_cache_mode(section->ctx, SSL_SESS_CACHE_OFF); + } + s_log(LOG_INFO, "Session resumption %s", section->option.session_resume + ? "enabled" : "disabled"); + SSL_CTX_sess_set_cache_size(section->ctx, section->session_size); + SSL_CTX_set_timeout(section->ctx, section->session_timeout); + SSL_CTX_sess_set_new_cb(section->ctx, sess_new_cb); + SSL_CTX_sess_set_get_cb(section->ctx, sess_get_cb); + SSL_CTX_sess_set_remove_cb(section->ctx, sess_remove_cb); + + /* set info callback */ + SSL_CTX_set_info_callback(section->ctx, info_callback); + + /* load certificate and private key to be verified by the peer server */ + if(auth_init(section)) + return 1; /* FAILED */ + + /* initialize verification of the peer server certificate */ + if(verify_init(section)) + return 1; /* FAILED */ + + /* initialize the DH/ECDH key agreement */ +#ifndef OPENSSL_NO_TLSEXT + if(!section->option.client) + SSL_CTX_set_tlsext_servername_callback(section->ctx, servername_cb); +#endif /* OPENSSL_NO_TLSEXT */ +#ifndef OPENSSL_NO_DH + dh_init(section); /* ignore the result (errors are not critical) */ +#endif /* OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH + if(ecdh_init(section)) + return 1; /* FAILED */ +#endif /* OPENSSL_NO_ECDH */ + + return 0; /* OK */ +} + +/**************************************** SNI callback */ + +#ifndef OPENSSL_NO_TLSEXT + +NOEXPORT int servername_cb(SSL *ssl, int *ad, void *arg) { + const char *servername=SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + CLI *c=SSL_get_ex_data(ssl, index_ssl_cli); + SERVERNAME_LIST *list; + + /* leave the alert type at SSL_AD_UNRECOGNIZED_NAME */ + (void)ad; /* squash the unused parameter warning */ + (void)arg; /* squash the unused parameter warning */ + + /* handle trivial cases first */ + if(!c->opt->servername_list_head) { + s_log(LOG_DEBUG, "SNI: no virtual services defined"); + return SSL_TLSEXT_ERR_OK; + } + if(!servername) { + s_log(LOG_NOTICE, "SNI: no servername received"); + return SSL_TLSEXT_ERR_NOACK; + } + + /* find a matching section */ + s_log(LOG_INFO, "SNI: requested servername: %s", servername); + for(list=c->opt->servername_list_head; list; list=list->next) + if(matches_wildcard(servername, list->servername)) + break; + if(!list) { + s_log(LOG_ERR, "SNI: no pattern matched servername: %s", servername); + return SSL_TLSEXT_ERR_OK; + } + s_log(LOG_DEBUG, "SNI: matched pattern: %s", list->servername); + + /* switch to the new section */ +#ifndef USE_FORK + service_up_ref(list->opt); + service_free(c->opt); +#endif + c->opt=list->opt; + SSL_set_SSL_CTX(ssl, c->opt->ctx); + SSL_set_verify(ssl, SSL_CTX_get_verify_mode(c->opt->ctx), + SSL_CTX_get_verify_callback(c->opt->ctx)); + s_log(LOG_NOTICE, "SNI: switched to service [%s]", c->opt->servname); +#ifdef USE_LIBWRAP + libwrap_auth(c); /* retry on a service switch */ +#endif /* USE_LIBWRAP */ + return SSL_TLSEXT_ERR_OK; +} +/* TLSEXT callback return codes: + * - SSL_TLSEXT_ERR_OK + * - SSL_TLSEXT_ERR_ALERT_WARNING + * - SSL_TLSEXT_ERR_ALERT_FATAL + * - SSL_TLSEXT_ERR_NOACK */ + +NOEXPORT int matches_wildcard(const char *servername, const char *pattern) { + if(!servername || !pattern) + return 0; + if(*pattern=='*') { /* wildcard comparison */ + ssize_t diff=(ssize_t)strlen(servername)-((ssize_t)strlen(pattern)-1); + if(diff<0) /* pattern longer than servername */ + return 0; + return !strcasecmp(servername+diff, pattern+1); + } else { /* string comparison */ + return !strcasecmp(servername, pattern); + } +} + +#endif /* OPENSSL_NO_TLSEXT */ + +/**************************************** DH initialization */ + +#ifndef OPENSSL_NO_DH + +#if OPENSSL_VERSION_NUMBER<0x10100000L +NOEXPORT STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) { + return ctx->cipher_list; +} +#endif + +NOEXPORT int dh_init(SERVICE_OPTIONS *section) { + DH *dh=NULL; + int i, n; + char description[128]; + STACK_OF(SSL_CIPHER) *ciphers; + + section->option.dh_temp_params=0; /* disable by default */ + + /* check if DH is needed for this section */ + if(section->option.client) { + s_log(LOG_INFO, "DH initialization skipped: client section"); + return 0; /* OK */ + } + ciphers=SSL_CTX_get_ciphers(section->ctx); + if(!ciphers) + return 1; /* ERROR (unlikely) */ + n=sk_SSL_CIPHER_num(ciphers); + for(i=0; iengine) /* cert is a file and not an identifier */ +#endif + dh=dh_read(section->cert); + if(dh) { + SSL_CTX_set_tmp_dh(section->ctx, dh); + s_log(LOG_INFO, "%d-bit DH parameters loaded", 8*DH_size(dh)); + DH_free(dh); + return 0; /* OK */ + } + CRYPTO_THREAD_read_lock(stunnel_locks[LOCK_DH]); + SSL_CTX_set_tmp_dh(section->ctx, dh_params); + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_DH]); + dh_temp_params=1; /* generate temporary DH parameters in cron */ + section->option.dh_temp_params=1; /* update this section in cron */ + s_log(LOG_INFO, "Using dynamic DH parameters"); + return 0; /* OK */ +} + +NOEXPORT DH *dh_read(char *cert) { + DH *dh; + BIO *bio; + + if(!cert) { + s_log(LOG_DEBUG, "No certificate available to load DH parameters"); + return NULL; /* FAILED */ + } + bio=BIO_new_file(cert, "r"); + if(!bio) { + sslerror("BIO_new_file"); + return NULL; /* FAILED */ + } + dh=PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if(!dh) { + while(ERR_get_error()) + ; /* OpenSSL error queue cleanup */ + s_log(LOG_DEBUG, "Could not load DH parameters from %s", cert); + return NULL; /* FAILED */ + } + s_log(LOG_DEBUG, "Using DH parameters from %s", cert); + return dh; +} + +#endif /* OPENSSL_NO_DH */ + +/**************************************** ECDH initialization */ + +#ifndef OPENSSL_NO_ECDH + +#if OPENSSL_VERSION_NUMBER < 0x10101000L +/* simplified version that only supports a single curve */ +NOEXPORT int SSL_CTX_set1_groups_list(SSL_CTX *ctx, char *list) { + int nid; + EC_KEY *ecdh; + + nid=OBJ_txt2nid(list); + if(nid==NID_undef) { + s_log(LOG_ERR, "Unsupported curve: %s", list); + return 0; /* FAILED */ + } + ecdh=EC_KEY_new_by_curve_name(nid); + if(!ecdh) { + sslerror("EC_KEY_new_by_curve_name"); + return 0; /* FAILED */ + } + if(!SSL_CTX_set_tmp_ecdh(ctx, ecdh)) { + sslerror("SSL_CTX_set_tmp_ecdhSSL_CTX_set_tmp_ecdh"); + EC_KEY_free(ecdh); + return 0; /* FAILED */ + } + EC_KEY_free(ecdh); + return 1; /* OK */ +} +#endif /* OpenSSL version < 1.1.1 */ + +NOEXPORT int ecdh_init(SERVICE_OPTIONS *section) { + s_log(LOG_DEBUG, "ECDH initialization"); + if(!SSL_CTX_set1_groups_list(section->ctx, section->curves)) { + s_log(LOG_ERR, "Invalid groups list in 'curves'"); + return 1; /* FAILED */ + } + s_log(LOG_DEBUG, "ECDH initialized with curves %s", section->curves); + return 0; /* OK */ +} + +#endif /* OPENSSL_NO_ECDH */ + +/**************************************** initialize OpenSSL CONF */ + +NOEXPORT int conf_init(SERVICE_OPTIONS *section) { +#if OPENSSL_VERSION_NUMBER>=0x10002000L + SSL_CONF_CTX *cctx; + NAME_LIST *curr; + char *cmd, *param; + + if(!section->config) + return 0; /* OK */ + cctx=SSL_CONF_CTX_new(); + if(!cctx) { + sslerror("SSL_CONF_CTX_new"); + return 1; /* FAILED */ + } + SSL_CONF_CTX_set_ssl_ctx(cctx, section->ctx); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_flags(cctx, section->option.client ? + SSL_CONF_FLAG_CLIENT : SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + + for(curr=section->config; curr; curr=curr->next) { + cmd=str_dup(curr->name); + param=strchr(cmd, ':'); + if(param) + *param++='\0'; + switch(SSL_CONF_cmd(cctx, cmd, param)) { + case 2: + s_log(LOG_DEBUG, "OpenSSL config \"%s\" set to \"%s\"", cmd, param); + break; + case 1: + s_log(LOG_DEBUG, "OpenSSL config command \"%s\" executed", cmd); + break; + case -2: + s_log(LOG_ERR, + "OpenSSL config command \"%s\" was not recognised", cmd); + str_free(cmd); + SSL_CONF_CTX_free(cctx); + return 1; /* FAILED */ + case -3: + s_log(LOG_ERR, + "OpenSSL config command \"%s\" requires a parameter", cmd); + str_free(cmd); + SSL_CONF_CTX_free(cctx); + return 1; /* FAILED */ + default: + sslerror("SSL_CONF_cmd"); + str_free(cmd); + SSL_CONF_CTX_free(cctx); + return 1; /* FAILED */ + } + str_free(cmd); + } + + if(!SSL_CONF_CTX_finish(cctx)) { + sslerror("SSL_CONF_CTX_finish"); + SSL_CONF_CTX_free(cctx); + return 1; /* FAILED */ + } + SSL_CONF_CTX_free(cctx); +#else /* OpenSSL earlier than 1.0.2 */ + (void)section; /* squash the unused parameter warning */ +#endif /* OpenSSL 1.0.2 or later */ + return 0; /* OK */ +} + +/**************************************** initialize authentication */ + +NOEXPORT int auth_init(SERVICE_OPTIONS *section) { + int cert_needed=1, key_needed=1; + + /* initialize PSK */ +#ifndef OPENSSL_NO_PSK + if(section->psk_keys) { + if(section->option.client) + SSL_CTX_set_psk_client_callback(section->ctx, psk_client_callback); + else + SSL_CTX_set_psk_server_callback(section->ctx, psk_server_callback); + } +#endif /* !defined(OPENSSL_NO_PSK) */ + + /* initialize the client cert engine */ +#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_VERSION_NUMBER>=0x0090809fL + /* SSL_CTX_set_client_cert_engine() was introduced in OpenSSL 0.9.8i */ + if(section->option.client && section->engine) { + if(SSL_CTX_set_client_cert_engine(section->ctx, section->engine)) { + s_log(LOG_INFO, "Client certificate engine (%s) enabled", + ENGINE_get_id(section->engine)); + } else { /* no client certificate functionality in this engine */ + while(ERR_get_error()) + ; /* OpenSSL error queue cleanup */ + s_log(LOG_INFO, "Client certificate engine (%s) not supported", + ENGINE_get_id(section->engine)); + } + } +#endif + + /* load the certificate and private key */ + if(!section->cert || !section->key) { + s_log(LOG_DEBUG, "No certificate or private key specified"); + return 0; /* OK */ + } +#ifndef OPENSSL_NO_ENGINE + if(section->engine) { /* try to use the engine first */ + cert_needed=load_cert_engine(section); + key_needed=load_key_engine(section); + } +#endif + if (cert_needed && pkcs12_extension(section->cert)) { + if (load_pkcs12_file(section)) { + return 1; /* FAILED */ + } + cert_needed=key_needed=0; /* don't load any PEM files */ + } + if(cert_needed && load_cert_file(section)) + return 1; /* FAILED */ + if(key_needed && load_key_file(section)) + return 1; /* FAILED */ + + /* validate the private key against the certificate */ + if(!SSL_CTX_check_private_key(section->ctx)) { + sslerror("Private key does not match the certificate"); + return 1; /* FAILED */ + } + s_log(LOG_DEBUG, "Private key check succeeded"); + return 0; /* OK */ +} + +#ifndef OPENSSL_NO_PSK + +NOEXPORT unsigned psk_client_callback(SSL *ssl, const char *hint, + char *identity, unsigned max_identity_len, + unsigned char *psk, unsigned max_psk_len) { + CLI *c; + size_t identity_len; + + (void)hint; /* squash the unused parameter warning */ + c=SSL_get_ex_data(ssl, index_ssl_cli); + if(!c->opt->psk_selected) { + s_log(LOG_ERR, "INTERNAL ERROR: No PSK identity selected"); + return 0; + } + /* the source seems to have its buffer large enough for + * the trailing null character, but the manual page says + * nothing about it -- lets play safe */ + identity_len=strlen(c->opt->psk_selected->identity)+1; + if(identity_len>max_identity_len) { + s_log(LOG_ERR, "PSK identity too long (%lu>%d bytes)", + (long unsigned)identity_len, max_psk_len); + return 0; + } + if(c->opt->psk_selected->key_len>max_psk_len) { + s_log(LOG_ERR, "PSK too long (%lu>%d bytes)", + (long unsigned)c->opt->psk_selected->key_len, max_psk_len); + return 0; + } + strcpy(identity, c->opt->psk_selected->identity); + memcpy(psk, c->opt->psk_selected->key_val, c->opt->psk_selected->key_len); + s_log(LOG_INFO, "PSK client configured for identity \"%s\"", identity); + return (unsigned)(c->opt->psk_selected->key_len); +} + +NOEXPORT unsigned psk_server_callback(SSL *ssl, const char *identity, + unsigned char *psk, unsigned max_psk_len) { + CLI *c; + PSK_KEYS *found; + + c=SSL_get_ex_data(ssl, index_ssl_cli); + found=psk_find(&c->opt->psk_sorted, identity); + if(!found) { + s_log(LOG_INFO, "PSK identity not found (session resumption?)"); + return 0; + } + if(found->key_len>max_psk_len) { + s_log(LOG_ERR, "PSK too long (%u>%u)", found->key_len, max_psk_len); + return 0; + } + memcpy(psk, found->key_val, found->key_len); + s_log(LOG_NOTICE, "Key configured for PSK identity \"%s\"", identity); + c->flag.psk=1; + return found->key_len; +} + +NOEXPORT int psk_compar(const void *a, const void *b) { + const PSK_KEYS *x=*(PSK_KEYS *const*)a, *y=*(PSK_KEYS *const*)b; + +#if 0 + s_log(LOG_DEBUG, "PSK cmp: %s %s", x->identity, y->identity); +#endif + return strcmp(x->identity, y->identity); +} + +void psk_sort(PSK_TABLE *table, PSK_KEYS *head) { + PSK_KEYS *curr; + size_t i; + + table->num=0; + for(curr=head; curr; curr=curr->next) + ++table->num; + s_log(LOG_INFO, "PSK identities: %lu retrieved", + (long unsigned)table->num); + table->val=str_alloc_detached(table->num*sizeof(PSK_KEYS *)); + for(curr=head, i=0; inum; ++i) { + table->val[i]=curr; + curr=curr->next; + } + qsort(table->val, table->num, sizeof(PSK_KEYS *), psk_compar); +#if 0 + for(i=0; inum; ++i) + s_log(LOG_DEBUG, "PSK table: %s", table->val[i]->identity); +#endif +} + +PSK_KEYS *psk_find(const PSK_TABLE *table, const char *identity) { + PSK_KEYS key, *ptr=&key, **ret; + + key.identity=identity; + ret=bsearch(&ptr, + table->val, table->num, sizeof(PSK_KEYS *), psk_compar); + return ret ? *ret : NULL; +} + +#endif /* !defined(OPENSSL_NO_PSK) */ + +NOEXPORT int pkcs12_extension(const char *filename) { + const char *ext=strrchr(filename, '.'); + return ext && (!strcasecmp(ext, ".p12") || !strcasecmp(ext, ".pfx")); +} + +NOEXPORT int load_pkcs12_file(SERVICE_OPTIONS *section) { + size_t len; + int i, success; + BIO *bio=NULL; + PKCS12 *p12=NULL; + X509 *cert=NULL; + STACK_OF(X509) *ca=NULL; + EVP_PKEY *pkey=NULL; + char pass[PEM_BUFSIZE]; + + s_log(LOG_INFO, "Loading certificate and private key from file: %s", + section->cert); + if(file_permissions(section->cert)) + return 1; /* FAILED */ + + bio=BIO_new_file(section->cert, "rb"); + if(!bio) { + sslerror("BIO_new_file"); + return 1; /* FAILED */ + } + p12=d2i_PKCS12_bio(bio, NULL); + if(!p12) { + sslerror("d2i_PKCS12_bio"); + BIO_free(bio); + return 1; /* FAILED */ + } + BIO_free(bio); + + /* try the cached value first */ + set_prompt(section->cert); + len=(size_t)cache_passwd_get_cb(pass, sizeof pass, 0, NULL); + if(len>=sizeof pass) + len=sizeof pass-1; + pass[len]='\0'; /* null-terminate */ + success=PKCS12_parse(p12, pass, &pkey, &cert, &ca); + + /* invoke the UI */ + for(i=0; !success && i<3; i++) { + if(!ui_retry()) + break; + if(i==0) { /* silence the cached attempt */ + ERR_clear_error(); + } else { + sslerror_queue(); /* dump the error queue */ + s_log(LOG_ERR, "Wrong passphrase: retrying"); + } + /* invoke the UI on subsequent calls */ + len=(size_t)cache_passwd_set_cb(pass, sizeof pass, 0, NULL); + if(len>=sizeof pass) + len=sizeof pass-1; + pass[len]='\0'; /* null-terminate */ + success=PKCS12_parse(p12, pass, &pkey, &cert, &ca); + } + if(!success) { + sslerror("PKCS12_parse"); + PKCS12_free(p12); + return 1; /* FAILED */ + } + + PKCS12_free(p12); + + if(!SSL_CTX_use_certificate(section->ctx, cert)) { + sslerror("SSL_CTX_use_certificate"); + return 1; /* FAILED */ + } + if(!SSL_CTX_use_PrivateKey(section->ctx, pkey)) { + sslerror("SSL_CTX_use_PrivateKey"); + return 1; /* FAILED */ + } + s_log(LOG_INFO, "Certificate and private key loaded from file: %s", + section->cert); + return 0; /* OK */ +} + +NOEXPORT int load_cert_file(SERVICE_OPTIONS *section) { + s_log(LOG_INFO, "Loading certificate from file: %s", section->cert); + if(!SSL_CTX_use_certificate_chain_file(section->ctx, section->cert)) { + sslerror("SSL_CTX_use_certificate_chain_file"); + return 1; /* FAILED */ + } + s_log(LOG_INFO, "Certificate loaded from file: %s", section->cert); + return 0; /* OK */ +} + +NOEXPORT int load_key_file(SERVICE_OPTIONS *section) { + int i, success; + + s_log(LOG_INFO, "Loading private key from file: %s", section->key); + if(file_permissions(section->key)) + return 1; /* FAILED */ + + /* try the cached value first */ + set_prompt(section->key); + SSL_CTX_set_default_passwd_cb(section->ctx, cache_passwd_get_cb); + success=SSL_CTX_use_PrivateKey_file(section->ctx, section->key, + SSL_FILETYPE_PEM); + /* invoke the UI on subsequent calls */ + SSL_CTX_set_default_passwd_cb(section->ctx, cache_passwd_set_cb); + + /* invoke the UI */ + for(i=0; !success && i<3; i++) { + if(!ui_retry()) + break; + if(i==0) { /* silence the cached attempt */ + ERR_clear_error(); + } else { + sslerror_queue(); /* dump the error queue */ + s_log(LOG_ERR, "Wrong passphrase: retrying"); + } + success=SSL_CTX_use_PrivateKey_file(section->ctx, section->key, + SSL_FILETYPE_PEM); + } + if(!success) { + sslerror("SSL_CTX_use_PrivateKey_file"); + return 1; /* FAILED */ + } + s_log(LOG_INFO, "Private key loaded from file: %s", section->key); + return 0; /* OK */ +} + +#ifndef OPENSSL_NO_ENGINE + +NOEXPORT int load_cert_engine(SERVICE_OPTIONS *section) { + struct { + const char *id; + X509 *cert; + } parms; + + s_log(LOG_INFO, "Loading certificate from engine ID: %s", section->cert); + parms.id=section->cert; + parms.cert=NULL; + ENGINE_ctrl_cmd(section->engine, "LOAD_CERT_CTRL", 0, &parms, NULL, 1); + if(!parms.cert) { + sslerror("ENGINE_ctrl_cmd"); + return 1; /* FAILED */ + } + if(!SSL_CTX_use_certificate(section->ctx, parms.cert)) { + sslerror("SSL_CTX_use_certificate"); + return 1; /* FAILED */ + } + s_log(LOG_INFO, "Certificate loaded from engine ID: %s", section->cert); + return 0; /* OK */ +} + +UI_METHOD *ui_stunnel() { + static UI_METHOD *ui_method=NULL; + + if(ui_method) /* already initialized */ + return ui_method; + ui_method=UI_create_method("stunnel UI"); + if(!ui_method) { + sslerror("UI_create_method"); + return NULL; + } + UI_method_set_opener(ui_method, ui_get_opener()); + UI_method_set_writer(ui_method, ui_get_writer()); + UI_method_set_reader(ui_method, ui_get_reader()); + UI_method_set_closer(ui_method, ui_get_closer()); + return ui_method; +} + +NOEXPORT int load_key_engine(SERVICE_OPTIONS *section) { + int i; + EVP_PKEY *pkey; + + s_log(LOG_INFO, "Initializing private key on engine ID: %s", section->key); + + /* do not use caching for engine PINs to prevent device lockout */ + SSL_CTX_set_default_passwd_cb(section->ctx, ui_passwd_cb); + + for(i=0; i<3; i++) { + pkey=ENGINE_load_private_key(section->engine, section->key, + ui_stunnel(), NULL); + if(!pkey) { + if(i<2 && ui_retry()) { /* wrong PIN */ + sslerror_queue(); /* dump the error queue */ + s_log(LOG_ERR, "Wrong PIN: retrying"); + continue; + } + sslerror("ENGINE_load_private_key"); + return 1; /* FAILED */ + } + if(SSL_CTX_use_PrivateKey(section->ctx, pkey)) + break; /* success */ + sslerror("SSL_CTX_use_PrivateKey"); + return 1; /* FAILED */ + } + s_log(LOG_INFO, "Private key initialized on engine ID: %s", section->key); + return 0; /* OK */ +} + +#endif /* !defined(OPENSSL_NO_ENGINE) */ + +/* additional caching layer on top of ui_passwd_cb() */ + +/* retrieve the cached passwd */ +NOEXPORT int cache_passwd_get_cb(char *buf, int size, + int rwflag, void *userdata) { + int len=cached_len; + + (void)rwflag; /* squash the unused parameter warning */ + (void)userdata; /* squash the unused parameter warning */ + if(len<0 || size<0) /* the API uses signed integers */ + return 0; + if(len>size) /* truncate the returned data if needed */ + len=size; + memcpy(buf, cached_passwd, (size_t)len); + return len; +} + +/* cache the passwd retrieved from UI */ +NOEXPORT int cache_passwd_set_cb(char *buf, int size, + int rwflag, void *userdata) { + memset(cached_passwd, 0, sizeof cached_passwd); + cached_len=ui_passwd_cb(cached_passwd, sizeof cached_passwd, + rwflag, userdata); + return cache_passwd_get_cb(buf, size, rwflag, userdata); +} + +NOEXPORT void set_prompt(const char *name) { + char *prompt; + + prompt=str_printf("Enter %s pass phrase:", name); + EVP_set_pw_prompt(prompt); + str_free(prompt); +} + +NOEXPORT int ui_retry() { + unsigned long err=ERR_peek_error(); + + switch(ERR_GET_LIB(err)) { + case ERR_LIB_EVP: /* 6 */ + switch(ERR_GET_REASON(err)) { + case EVP_R_BAD_DECRYPT: + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_EVP error reason: %d", + ERR_GET_REASON(err)); + return 0; + } + case ERR_LIB_PEM: /* 9 */ + switch(ERR_GET_REASON(err)) { + case PEM_R_BAD_PASSWORD_READ: + case PEM_R_BAD_DECRYPT: + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_PEM error reason: %d", + ERR_GET_REASON(err)); + return 0; + } + case ERR_LIB_ASN1: /* 13 */ + return 1; + case ERR_LIB_PKCS12: /* 35 */ + switch(ERR_GET_REASON(err)) { + case PKCS12_R_MAC_VERIFY_FAILURE: + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_PKCS12 error reason: %d", + ERR_GET_REASON(err)); + return 0; + } +#ifdef ERR_LIB_DSO /* 37 */ + case ERR_LIB_DSO: + return 1; +#endif + case ERR_LIB_UI: /* 40 */ + switch(ERR_GET_REASON(err)) { + case UI_R_RESULT_TOO_LARGE: + case UI_R_RESULT_TOO_SMALL: +#ifdef UI_R_PROCESSING_ERROR + case UI_R_PROCESSING_ERROR: +#endif + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_UI error reason: %d", + ERR_GET_REASON(err)); + return 0; + } +#ifdef ERR_LIB_OSSL_STORE + case ERR_LIB_OSSL_STORE: /* 44 - added in OpenSSL 1.1.1 */ + switch(ERR_GET_REASON(err)) { + case OSSL_STORE_R_BAD_PASSWORD_READ: + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_OSSL_STORE error reason: %d", + ERR_GET_REASON(err)); + return 0; + } +#endif +#ifdef ERR_LIB_PROV + case ERR_LIB_PROV: /* 57 - added in OpenSSL 3.0 */ + switch(ERR_GET_REASON(err)) { + case PROV_R_BAD_DECRYPT: + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_PROV error reason: %d", + ERR_GET_REASON(err)); + return 0; + } +#endif + case ERR_LIB_USER: /* 128 - PKCS#11 hacks */ + switch(ERR_GET_REASON(err)) { + case 7UL: /* CKR_ARGUMENTS_BAD */ + case 0xa0UL: /* CKR_PIN_INCORRECT */ + return 1; + default: + s_log(LOG_ERR, "Unhandled ERR_LIB_USER error reason: %d", + ERR_GET_REASON(err)); + return 0; + } + default: + s_log(LOG_ERR, "Unhandled error library: %d", ERR_GET_LIB(err)); + return 0; + } +} + +/**************************************** session tickets */ + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + +typedef struct { + void *session_authenticated; +#if 0 + SOCKADDR_UNION addr; +#endif +} TICKET_DATA; + +NOEXPORT int generate_session_ticket_cb(SSL *ssl, void *arg) { + SSL_SESSION *sess; + TICKET_DATA ticket_data; +#if 0 + SOCKADDR_UNION *addr; +#endif + int retval; + + (void)arg; /* squash the unused parameter warning */ + + s_log(LOG_DEBUG, "Generate session ticket callback"); + + sess=SSL_get1_session(ssl); + if(!sess) + return 0; + memset(&ticket_data, 0, sizeof(TICKET_DATA)); + + ticket_data.session_authenticated= + SSL_SESSION_get_ex_data(sess, index_session_authenticated); + +#if 0 + /* TODO: add remote_start() invocation here */ + CRYPTO_THREAD_read_lock(stunnel_locks[LOCK_ADDR]); + addr=SSL_SESSION_get_ex_data(sess, index_session_connect_address); + if(addr) + memcpy(&ticket_data.addr, addr, (size_t)addr_len(addr)); + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_ADDR]); +#endif + + retval=SSL_SESSION_set1_ticket_appdata(sess, + &ticket_data, sizeof(TICKET_DATA)); + SSL_SESSION_free(sess); + return retval; +} + +NOEXPORT int decrypt_session_ticket_cb(SSL *ssl, SSL_SESSION *sess, + const unsigned char *keyname, size_t keyname_len, + SSL_TICKET_STATUS status, void *arg) { + TICKET_DATA *ticket_data; + size_t ticket_len; + + (void)ssl; /* squash the unused parameter warning */ + (void)keyname; /* squash the unused parameter warning */ + (void)keyname_len; /* squash the unused parameter warning */ + (void)arg; /* squash the unused parameter warning */ + + s_log(LOG_DEBUG, "Decrypt session ticket callback"); + + switch(status) { + case SSL_TICKET_EMPTY: + case SSL_TICKET_NO_DECRYPT: + return SSL_TICKET_RETURN_IGNORE_RENEW; + case SSL_TICKET_SUCCESS: + case SSL_TICKET_SUCCESS_RENEW: + break; + default: + return SSL_TICKET_RETURN_ABORT; + } + + if(!SSL_SESSION_get0_ticket_appdata(sess, + (void **)&ticket_data, &ticket_len)) { + s_log(LOG_WARNING, "Failed to get ticket application data"); + return SSL_TICKET_RETURN_IGNORE_RENEW; + } + if(!ticket_data) { + s_log(LOG_WARNING, "Invalid ticket application data value"); + return SSL_TICKET_RETURN_IGNORE_RENEW; + } + if(ticket_len != sizeof(TICKET_DATA)) { + s_log(LOG_WARNING, "Invalid ticket application data length"); + return SSL_TICKET_RETURN_IGNORE_RENEW; + } + + s_log(LOG_INFO, "Decrypted ticket for an authenticated session: %s", + ticket_data->session_authenticated ? "yes" : "no"); + SSL_SESSION_set_ex_data(sess, index_session_authenticated, + ticket_data->session_authenticated); + +#if 0 + if(ticket_data->addr.sa.sa_family) { + char *addr_txt; + SOCKADDR_UNION *old_addr; + + addr_txt=s_ntop(&ticket_data->addr, addr_len(&ticket_data->addr)); + s_log(LOG_INFO, "Decrypted ticket persistence address: %s", addr_txt); + str_free(addr_txt); + CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_ADDR]); + old_addr=SSL_SESSION_get_ex_data(sess, index_session_connect_address); + if(SSL_SESSION_set_ex_data(sess, index_session_connect_address, &ticket_data->addr)) { + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_ADDR]); + str_free(old_addr); /* NULL pointers are ignored */ + } else { /* failed to store ticket_data->addr */ + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_ADDR]); + sslerror("SSL_SESSION_set_ex_data"); + } + } else { + s_log(LOG_INFO, "Decrypted ticket did not include a persistence address"); + } +#endif + + switch(status) { + case SSL_TICKET_SUCCESS: + return SSL_TICKET_RETURN_USE; + case SSL_TICKET_SUCCESS_RENEW: + return SSL_TICKET_RETURN_USE_RENEW; + } + return SSL_TICKET_RETURN_ABORT; /* it should never get executed */ +} +#endif + +#if OPENSSL_VERSION_NUMBER>=0x10000000L +NOEXPORT int ssl_tlsext_ticket_key_cb(SSL *ssl, unsigned char *key_name, + unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { + CLI *c; + const EVP_CIPHER *cipher; + int iv_len; + + (void)key_name; /* squash the unused parameter warning */ + s_log(LOG_DEBUG, "Session ticket processing callback"); + + c=SSL_get_ex_data(ssl, index_ssl_cli); + if(!HMAC_Init_ex(hctx, (const unsigned char *)(c->opt->ticket_mac->key_val), + c->opt->ticket_mac->key_len, EVP_sha256(), NULL)) { + s_log(LOG_ERR, "HMAC_Init_ex failed"); + return -1; + } + if(c->opt->ticket_key->key_len == 16) + cipher=EVP_aes_128_cbc(); + else /* c->opt->ticket_key->key_len == 32 */ + cipher=EVP_aes_256_cbc(); + if(enc) { /* create new session */ + /* EVP_CIPHER_iv_length() returns 16 for either cipher EVP_aes_128_cbc() or EVP_aes_256_cbc() */ + iv_len=EVP_CIPHER_iv_length(cipher); + if(RAND_bytes(iv, iv_len) <= 0) { /* RAND_bytes error */ + s_log(LOG_ERR, "RAND_bytes failed"); + return -1; + } + if(!EVP_EncryptInit_ex(ctx, cipher, NULL, + (const unsigned char *)(c->opt->ticket_key->key_val), iv)) { + s_log(LOG_ERR, "EVP_EncryptInit_ex failed"); + return -1; + } + } else /* retrieve session */ + if(!EVP_DecryptInit_ex(ctx, cipher, NULL, + (const unsigned char *)(c->opt->ticket_key->key_val), iv)) { + s_log(LOG_ERR, "EVP_DecryptInit_ex failed"); + return -1; + } + /* By default, in TLSv1.2 and below, a new session ticket */ + /* is not issued on a successful resumption. */ + /* In TLSv1.3 the default behaviour is to always issue a new ticket on resumption. */ + /* This behaviour can NOT be changed if this ticket key callback is in use! */ + if(strcmp(SSL_get_version(c->ssl), "TLSv1.3")) + return 1; /* new session ticket is not issued */ + else + return 2; /* session ticket should be replaced */ +} +#endif /* OpenSSL 1.0.0 or later */ + +/**************************************** session callbacks */ + +NOEXPORT int sess_new_cb(SSL *ssl, SSL_SESSION *sess) { + CLI *c; + + s_log(LOG_DEBUG, "New session callback"); + c=SSL_get_ex_data(ssl, index_ssl_cli); + + new_chain(c); /* new session -> we may have a new peer certificate chain */ + + if(c->opt->option.client) + session_cache_save(c, sess); + + if(c->opt->option.sessiond) + cache_new(ssl, sess); + + print_session_id(sess); + + return 0; /* the OpenSSL's manual is really bad -> use the source here */ +} + +#if OPENSSL_VERSION_NUMBER<0x0090800fL +NOEXPORT const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, + unsigned int *len) { + if(len) + *len=s->session_id_length; + return (const unsigned char *)s->session_id; +} +#endif + +void print_session_id(SSL_SESSION *sess) { + const unsigned char *session_id; + unsigned int session_id_length; + char session_id_txt[2*SSL_MAX_SSL_SESSION_ID_LENGTH+1]; + + session_id=SSL_SESSION_get_id(sess, &session_id_length); + bin2hexstring(session_id, session_id_length, + session_id_txt, sizeof session_id_txt); + s_log(LOG_INFO, "Session id: %s", session_id_txt); +} + +NOEXPORT void new_chain(CLI *c) { + BIO *bio; + int i, len; + X509 *peer_cert; + STACK_OF(X509) *sk; + char *chain; + + if(c->opt->chain) /* already cached */ + return; /* this race condition is safe to ignore */ + bio=BIO_new(BIO_s_mem()); + if(!bio) + return; + sk=SSL_get_peer_cert_chain(c->ssl); + for(i=0; sk && iopt->option.client) { + peer_cert=SSL_get_peer_certificate(c->ssl); + if(peer_cert) { + PEM_write_bio_X509(bio, peer_cert); + X509_free(peer_cert); + } + } + len=BIO_pending(bio); + if(len<=0) { + s_log(LOG_INFO, "No peer certificate received"); + BIO_free(bio); + return; + } + /* prevent automatic deallocation of the cached value */ + chain=str_alloc_detached((size_t)len+1); + len=BIO_read(bio, chain, len); + if(len<0) { + s_log(LOG_ERR, "BIO_read failed"); + BIO_free(bio); + str_free(chain); + return; + } + chain[len]='\0'; + BIO_free(bio); + c->opt->chain=chain; /* this race condition is safe to ignore */ + ui_new_chain(c->opt->section_number); + s_log(LOG_DEBUG, "Peer certificate was cached (%d bytes)", len); +} + +/* cache client sessions */ +NOEXPORT void session_cache_save(CLI *c, SSL_SESSION *sess) { + SSL_SESSION *old; + +#if OPENSSL_VERSION_NUMBER>=0x10100000L + SSL_SESSION_up_ref(sess); +#else + sess=SSL_get1_session(c->ssl); +#endif + + CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_SESSION]); + if(c->opt->option.delayed_lookup) { + old=c->opt->session; + c->opt->session=sess; + } else { /* per-destination client cache */ + if(c->opt->connect_session) { + old=c->opt->connect_session[c->idx]; + c->opt->connect_session[c->idx]=sess; + } else { + s_log(LOG_ERR, "INTERNAL ERROR: Uninitialized client session cache"); + old=NULL; + } + } + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_SESSION]); + + if(old) + SSL_SESSION_free(old); +} + +NOEXPORT SSL_SESSION *sess_get_cb(SSL *ssl, +#if OPENSSL_VERSION_NUMBER>=0x10100000L + const +#endif + unsigned char *key, int key_len, int *do_copy) { + CLI *c; + + s_log(LOG_DEBUG, "Get session callback"); + *do_copy=0; /* allow the session to be freed automatically */ + c=SSL_get_ex_data(ssl, index_ssl_cli); + if(c->opt->option.sessiond) + return cache_get(ssl, key, key_len); + return NULL; /* no session to resume */ +} + +NOEXPORT void sess_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) { + SERVICE_OPTIONS *opt; + + s_log(LOG_DEBUG, "Remove session callback"); + opt=SSL_CTX_get_ex_data(ctx, index_ssl_ctx_opt); + if(opt->option.sessiond) + cache_remove(ctx, sess); +} + +/**************************************** sessiond functionality */ + +#define CACHE_CMD_NEW 0x00 +#define CACHE_CMD_GET 0x01 +#define CACHE_CMD_REMOVE 0x02 +#define CACHE_RESP_ERR 0x80 +#define CACHE_RESP_OK 0x81 + +NOEXPORT void cache_new(SSL *ssl, SSL_SESSION *sess) { + unsigned char *val, *val_tmp; + ssize_t val_len; + const unsigned char *session_id; + unsigned int session_id_length; + + val_len=i2d_SSL_SESSION(sess, NULL); + val_tmp=val=str_alloc((size_t)val_len); + i2d_SSL_SESSION(sess, &val_tmp); + + session_id=SSL_SESSION_get_id(sess, &session_id_length); + cache_transfer(SSL_get_SSL_CTX(ssl), CACHE_CMD_NEW, + SSL_SESSION_get_timeout(sess), + session_id, session_id_length, val, (size_t)val_len, NULL, NULL); + str_free(val); +} + +NOEXPORT SSL_SESSION *cache_get(SSL *ssl, + const unsigned char *key, int key_len) { + unsigned char *val=NULL; + const unsigned char *val_tmp=NULL; + ssize_t val_len=0; + SSL_SESSION *sess; + + cache_transfer(SSL_get_SSL_CTX(ssl), CACHE_CMD_GET, 0, + key, (size_t)key_len, NULL, 0, &val, (size_t *)&val_len); + if(!val) + return NULL; + val_tmp=val; + sess=d2i_SSL_SESSION(NULL, &val_tmp, (long)val_len); + str_free(val); + return sess; +} + +NOEXPORT void cache_remove(SSL_CTX *ctx, SSL_SESSION *sess) { + const unsigned char *session_id; + unsigned int session_id_length; + + session_id=SSL_SESSION_get_id(sess, &session_id_length); + cache_transfer(ctx, CACHE_CMD_REMOVE, 0, + session_id, session_id_length, NULL, 0, NULL, NULL); +} + +#define MAX_VAL_LEN 512 +typedef struct { + u_char version, type; + u_short timeout; + u_char key[SSL_MAX_SSL_SESSION_ID_LENGTH]; + u_char val[MAX_VAL_LEN]; +} CACHE_PACKET; + +NOEXPORT void cache_transfer(SSL_CTX *ctx, const u_char type, + const long timeout, + const u_char *key, const size_t key_len, + const u_char *val, const size_t val_len, + unsigned char **ret, size_t *ret_len) { + char session_id_txt[2*SSL_MAX_SSL_SESSION_ID_LENGTH+1]; + const char *type_description[]={"new", "get", "remove"}; + SOCKET s; + ssize_t len; + struct timeval t; + CACHE_PACKET *packet; + SERVICE_OPTIONS *section; + + if(ret) /* set error as the default result if required */ + *ret=NULL; + + /* log the request information */ + bin2hexstring(key, key_len, session_id_txt, sizeof session_id_txt); + s_log(LOG_INFO, + "cache_transfer: request=%s, timeout=%ld, id=%s, length=%lu", + type_description[type], timeout, session_id_txt, (long unsigned)val_len); + + /* allocate UDP packet buffer */ + if(key_len>SSL_MAX_SSL_SESSION_ID_LENGTH) { + s_log(LOG_ERR, "cache_transfer: session id too big (%lu bytes)", + (unsigned long)key_len); + return; + } + if(val_len>MAX_VAL_LEN) { + s_log(LOG_ERR, "cache_transfer: encoded session too big (%lu bytes)", + (unsigned long)key_len); + return; + } + packet=str_alloc(sizeof(CACHE_PACKET)); + + /* setup packet */ + packet->version=1; + packet->type=type; + packet->timeout=htons((u_short)(timeout<64800?timeout:64800));/* 18 hours */ + memcpy(packet->key, key, key_len); + if(val && val_len) /* only check it to make code analysis tools happy */ + memcpy(packet->val, val, val_len); + + /* create the socket */ + s=s_socket(AF_INET, SOCK_DGRAM, 0, 0, "cache_transfer: socket"); + if(s==INVALID_SOCKET) { + str_free(packet); + return; + } + + /* retrieve pointer to the section structure of this ctx */ + section=SSL_CTX_get_ex_data(ctx, index_ssl_ctx_opt); + if(sendto(s, (void *)packet, +#ifdef USE_WIN32 + (int) +#endif + (sizeof(CACHE_PACKET)-MAX_VAL_LEN+val_len), + 0, §ion->sessiond_addr.sa, + addr_len(§ion->sessiond_addr))<0) { + sockerror("cache_transfer: sendto"); + closesocket(s); + str_free(packet); + return; + } + + if(!ret || !ret_len) { /* no response is required */ + closesocket(s); + str_free(packet); + return; + } + + /* set recvfrom timeout to 200ms */ + t.tv_sec=0; + t.tv_usec=200; + if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&t, sizeof t)<0) { + sockerror("cache_transfer: setsockopt SO_RCVTIMEO"); + closesocket(s); + str_free(packet); + return; + } + + /* retrieve response */ + len=recv(s, (void *)packet, sizeof(CACHE_PACKET), 0); + closesocket(s); + if(len<0) { + int err=get_last_socket_error(); + + if(err==S_EWOULDBLOCK || (S_EWOULDBLOCK!=S_EAGAIN && err==S_EAGAIN)) + s_log(LOG_INFO, "cache_transfer: recv timeout"); + else + sockerror("cache_transfer: recv"); + str_free(packet); + return; + } + + /* parse results */ + if(len<(int)sizeof(CACHE_PACKET)-MAX_VAL_LEN || /* too short */ + packet->version!=1 || /* wrong version */ + safe_memcmp(packet->key, key, key_len)) { /* wrong session id */ + s_log(LOG_DEBUG, "cache_transfer: malformed packet received"); + str_free(packet); + return; + } + if(packet->type!=CACHE_RESP_OK) { + s_log(LOG_INFO, "cache_transfer: session not found"); + str_free(packet); + return; + } + *ret_len=(size_t)len-(sizeof(CACHE_PACKET)-MAX_VAL_LEN); + *ret=str_alloc(*ret_len); + s_log(LOG_INFO, "cache_transfer: session found"); + memcpy(*ret, packet->val, *ret_len); + str_free(packet); +} + +/**************************************** informational callback */ + +NOEXPORT void info_callback(const SSL *ssl, int where, int ret) { + CLI *c; + SSL_CTX *ctx; + const char *state_string; + + c=SSL_get_ex_data(ssl, index_ssl_cli); + if(c) { +#if OPENSSL_VERSION_NUMBER>=0x10100000L + OSSL_HANDSHAKE_STATE state=SSL_get_state(ssl); +#else + int state=SSL_get_state((SSL *)ssl); +#endif + +#if 0 + s_log(LOG_DEBUG, "state = %x", state); +#endif + + /* log the client certificate request (if received) */ +#ifndef SSL3_ST_CR_CERT_REQ_A + if(state==TLS_ST_CR_CERT_REQ) +#else + if(state==SSL3_ST_CR_CERT_REQ_A) +#endif + print_client_CA_list(SSL_get_client_CA_list(ssl)); +#ifndef SSL3_ST_CR_SRVR_DONE_A + if(state==TLS_ST_CR_SRVR_DONE) +#else + if(state==SSL3_ST_CR_SRVR_DONE_A) +#endif + if(!SSL_get_client_CA_list(ssl)) + s_log(LOG_INFO, "Client certificate not requested"); + + /* prevent renegotiation DoS attack */ + if((where&SSL_CB_HANDSHAKE_DONE) + && c->reneg_state==RENEG_INIT) { + /* first (initial) handshake was completed, remember this, + * so that further renegotiation attempts can be detected */ + c->reneg_state=RENEG_ESTABLISHED; + } else if((where&SSL_CB_ACCEPT_LOOP) + && c->reneg_state==RENEG_ESTABLISHED) { +#ifndef SSL3_ST_SR_CLNT_HELLO_A + if(state==TLS_ST_SR_CLNT_HELLO) { +#else + if(state==SSL3_ST_SR_CLNT_HELLO_A + || state==SSL23_ST_SR_CLNT_HELLO_A) { +#endif + /* client hello received after initial handshake, + * this means renegotiation -> mark it */ + c->reneg_state=RENEG_DETECTED; + } + } + + if(c->opt->log_levelopt->option.client) { + s_log(LOG_DEBUG, "%6ld client connect(s) requested", + SSL_CTX_sess_connect(ctx)); + s_log(LOG_DEBUG, "%6ld client connect(s) succeeded", + SSL_CTX_sess_connect_good(ctx)); + s_log(LOG_DEBUG, "%6ld client renegotiation(s) requested", + SSL_CTX_sess_connect_renegotiate(ctx)); + } else { + s_log(LOG_DEBUG, "%6ld server accept(s) requested", + SSL_CTX_sess_accept(ctx)); + s_log(LOG_DEBUG, "%6ld server accept(s) succeeded", + SSL_CTX_sess_accept_good(ctx)); + s_log(LOG_DEBUG, "%6ld server renegotiation(s) requested", + SSL_CTX_sess_accept_renegotiate(ctx)); + } + /* according to the source it not only includes internal + and external session caches, but also session tickets */ + s_log(LOG_DEBUG, "%6ld session reuse(s)", + SSL_CTX_sess_hits(ctx)); + if(!c->opt->option.client) { /* server session cache stats */ + s_log(LOG_DEBUG, "%6ld internal session cache item(s)", + SSL_CTX_sess_number(ctx)); + s_log(LOG_DEBUG, "%6ld internal session cache fill-up(s)", + SSL_CTX_sess_cache_full(ctx)); + s_log(LOG_DEBUG, "%6ld internal session cache miss(es)", + SSL_CTX_sess_misses(ctx)); + s_log(LOG_DEBUG, "%6ld external session cache hit(s)", + SSL_CTX_sess_cb_hits(ctx)); + s_log(LOG_DEBUG, "%6ld expired session(s) retrieved", + SSL_CTX_sess_timeouts(ctx)); + } + } +} + +/**************************************** TLS error reporting */ + +void sslerror(const char *txt) { /* OpenSSL error handler */ + unsigned long err; + const char *file; + int line; + + err=ERR_get_error_line(&file, &line); + if(err) { + sslerror_queue(); + sslerror_log(err, file, line, txt); + } else { + s_log(LOG_ERR, "%s: Peer suddenly disconnected", txt); + } +} + +NOEXPORT void sslerror_queue(void) { /* recursive dump of the error queue */ + unsigned long err; + const char *file; + int line; + + err=ERR_get_error_line(&file, &line); + if(err) { + sslerror_queue(); + sslerror_log(err, file, line, "error queue"); + } +} + +NOEXPORT void sslerror_log(unsigned long err, + const char *file, int line, const char *txt) { + char *str; + + str=str_alloc(256); + ERR_error_string_n(err, str, 256); + s_log(LOG_ERR, "%s: %s:%d: %s", txt, file, line, str); + str_free(str); +} + +/* end of ctx.c */ diff --git a/tools/deps/custom_stunnel/src/options.c b/tools/deps/custom_stunnel/src/options.c new file mode 100644 index 000000000..988858811 --- /dev/null +++ b/tools/deps/custom_stunnel/src/options.c @@ -0,0 +1,4914 @@ +/* + * stunnel TLS offloading and load-balancing proxy + * Copyright (C) 1998-2022 Michal Trojnara + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Linking stunnel statically or dynamically with other modules is making + * a combined work based on stunnel. Thus, the terms and conditions of + * the GNU General Public License cover the whole combination. + * + * In addition, as a special exception, the copyright holder of stunnel + * gives you permission to combine stunnel with free software programs or + * libraries that are released under the GNU LGPL and with code included + * in the standard release of OpenSSL under the OpenSSL License (or + * modified versions of such code, with unchanged license). You may copy + * and distribute such a system following the terms of the GNU GPL for + * stunnel and the licenses of the other code concerned. + * + * Note that people who make modified versions of stunnel are not obligated + * to grant this special exception for their modified versions; it is their + * choice whether to do so. The GNU General Public License gives permission + * to release a modified version without this exception; this exception + * also makes it possible to release a modified version which carries + * forward this exception. + */ + +#include "prototypes.h" + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#define DEFAULT_CURVES "X25519:P-256:X448:P-521:P-384" +#else /* OpenSSL version < 1.1.1 */ +#define DEFAULT_CURVES "prime256v1" +#endif /* OpenSSL version >= 1.1.1 */ + +#if defined(_WIN32_WCE) && !defined(CONFDIR) +#define CONFDIR "\\stunnel" +#endif + +#define CONFLINELEN (16*1024) + +#define INVALID_SSL_OPTION ((long unsigned)-1) + +typedef enum { + CMD_SET_DEFAULTS, /* set default values */ + CMD_SET_COPY, /* duplicate from new_service_options */ + CMD_FREE, /* deallocate memory */ + CMD_SET_VALUE, /* set a user-specified value */ + CMD_INITIALIZE, /* initialize the global options or a section */ + CMD_PRINT_DEFAULTS, /* print default values */ + CMD_PRINT_HELP /* print help */ +} CMD; + +NOEXPORT int options_file(char *, CONF_TYPE, SERVICE_OPTIONS **); +NOEXPORT int init_section(int, SERVICE_OPTIONS **); +#ifdef USE_WIN32 +struct dirent { + char d_name[MAX_PATH]; +}; +int scandir(const char *, struct dirent ***, + int (*)(const struct dirent *), + int (*)(const struct dirent **, const struct dirent **)); +int alphasort(const struct dirent **, const struct dirent **); +#endif +NOEXPORT const char *parse_global_option(CMD, GLOBAL_OPTIONS *, char *, char *); +NOEXPORT const char *parse_service_option(CMD, SERVICE_OPTIONS **, char *, char *); + +#ifndef OPENSSL_NO_TLSEXT +NOEXPORT const char *sni_init(SERVICE_OPTIONS *); +NOEXPORT void sni_free(SERVICE_OPTIONS *); +#endif /* !defined(OPENSSL_NO_TLSEXT) */ + +#if OPENSSL_VERSION_NUMBER>=0x10100000L +NOEXPORT int str_to_proto_version(const char *); +#else /* OPENSSL_VERSION_NUMBER<0x10100000L */ +NOEXPORT char *tls_methods_set(SERVICE_OPTIONS *, const char *); +NOEXPORT char *tls_methods_check(SERVICE_OPTIONS *); +#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */ + +NOEXPORT const char *parse_debug_level(char *, SERVICE_OPTIONS *); + +#ifndef OPENSSL_NO_PSK +NOEXPORT PSK_KEYS *psk_read(char *); +NOEXPORT PSK_KEYS *psk_dup(PSK_KEYS *); +NOEXPORT void psk_free(PSK_KEYS *); +#endif /* !defined(OPENSSL_NO_PSK) */ + +#if OPENSSL_VERSION_NUMBER>=0x10000000L +NOEXPORT TICKET_KEY *key_read(char *, const char *); +NOEXPORT TICKET_KEY *key_dup(TICKET_KEY *); +NOEXPORT void key_free(TICKET_KEY *); +#endif /* OpenSSL 1.0.0 or later */ + +typedef struct { + const char *name; + uint64_t value; +} SSL_OPTION; + +static const SSL_OPTION ssl_opts[] = { +#ifdef SSL_OP_ALL + {"ALL", SSL_OP_ALL}, +#endif +#ifdef SSL_OP_ALLOW_CLIENT_RENEGOTIATION + {"ALLOW_CLIENT_RENEGOTIATION", SSL_OP_ALLOW_CLIENT_RENEGOTIATION}, +#endif +#ifdef SSL_OP_ALLOW_NO_DHE_KEX + {"ALLOW_NO_DHE_KEX", SSL_OP_ALLOW_NO_DHE_KEX}, +#endif +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + {"ALLOW_UNSAFE_LEGACY_RENEGOTIATION", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION}, +#endif +#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + {"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE}, +#endif +#ifdef SSL_OP_CISCO_ANYCONNECT + {"CISCO_ANYCONNECT", SSL_OP_CISCO_ANYCONNECT}, +#endif +#ifdef SSL_OP_CLEANSE_PLAINTEXT + {"CLEANSE_PLAINTEXT", SSL_OP_CLEANSE_PLAINTEXT}, +#endif +#ifdef SSL_OP_COOKIE_EXCHANGE + {"COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE}, +#endif +#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG + {"CRYPTOPRO_TLSEXT_BUG", SSL_OP_CRYPTOPRO_TLSEXT_BUG}, +#endif +#ifdef SSL_OP_DISABLE_TLSEXT_CA_NAMES + {"DISABLE_TLSEXT_CA_NAMES", SSL_OP_DISABLE_TLSEXT_CA_NAMES}, +#endif +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + {"DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS}, +#endif +#ifdef SSL_OP_ENABLE_KTLS + {"ENABLE_KTLS", SSL_OP_ENABLE_KTLS}, +#endif +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT + {"ENABLE_MIDDLEBOX_COMPAT", SSL_OP_ENABLE_MIDDLEBOX_COMPAT}, +#endif +#ifdef SSL_OP_EPHEMERAL_RSA + {"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA}, +#endif +#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF + {"IGNORE_UNEXPECTED_EOF", SSL_OP_IGNORE_UNEXPECTED_EOF}, +#endif +#ifdef SSL_OP_LEGACY_SERVER_CONNECT + {"LEGACY_SERVER_CONNECT", SSL_OP_LEGACY_SERVER_CONNECT}, +#endif +#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER + {"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER}, +#endif +#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG + {"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG}, +#endif +#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING + {"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING}, +#endif +#ifdef SSL_OP_NETSCAPE_CA_DN_BUG + {"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG}, +#endif +#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG + {"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG}, +#endif +#ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG + {"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG}, +#endif +#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG + {"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG}, +#endif +#ifdef SSL_OP_NO_ANTI_REPLAY + {"NO_ANTI_REPLAY", SSL_OP_NO_ANTI_REPLAY}, +#endif +#ifdef SSL_OP_NO_COMPRESSION + {"NO_COMPRESSION", SSL_OP_NO_COMPRESSION}, +#endif +#ifdef SSL_OP_NO_DTLS_MASK + {"NO_DTLS_MASK", SSL_OP_NO_DTLS_MASK}, +#endif +#ifdef SSL_OP_NO_DTLSv1 + {"NO_DTLSv1", SSL_OP_NO_DTLSv1}, +#endif +#ifdef SSL_OP_NO_DTLSv1_2 + {"NO_DTLSv1_2", SSL_OP_NO_DTLSv1_2}, +#endif +#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC + {"NO_ENCRYPT_THEN_MAC", SSL_OP_NO_ENCRYPT_THEN_MAC}, +#endif +#ifdef SSL_OP_NO_EXTENDED_MASTER_SECRET + {"NO_EXTENDED_MASTER_SECRET", SSL_OP_NO_EXTENDED_MASTER_SECRET}, +#endif +#ifdef SSL_OP_NO_QUERY_MTU + {"NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU}, +#endif +#ifdef SSL_OP_NO_RENEGOTIATION + {"NO_RENEGOTIATION", SSL_OP_NO_RENEGOTIATION}, +#endif +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + {"NO_SESSION_RESUMPTION_ON_RENEGOTIATION", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION}, +#endif +#ifdef SSL_OP_NO_SSL_MASK + {"NO_SSL_MASK", SSL_OP_NO_SSL_MASK}, +#endif +#ifdef SSL_OP_NO_SSLv2 + {"NO_SSLv2", SSL_OP_NO_SSLv2}, +#endif +#ifdef SSL_OP_NO_SSLv3 + {"NO_SSLv3", SSL_OP_NO_SSLv3}, +#endif +#ifdef SSL_OP_NO_TICKET + {"NO_TICKET", SSL_OP_NO_TICKET}, +#endif +#ifdef SSL_OP_NO_TLSv1 + {"NO_TLSv1", SSL_OP_NO_TLSv1}, +#endif +#ifdef SSL_OP_NO_TLSv1_1 + {"NO_TLSv1_1", SSL_OP_NO_TLSv1_1}, +#endif +#ifdef SSL_OP_NO_TLSv1_2 + {"NO_TLSv1_2", SSL_OP_NO_TLSv1_2}, +#endif +#ifdef SSL_OP_NO_TLSv1_3 + {"NO_TLSv1_3", SSL_OP_NO_TLSv1_3}, +#endif +#ifdef SSL_OP_PKCS1_CHECK_1 + {"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1}, +#endif +#ifdef SSL_OP_PKCS1_CHECK_2 + {"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2}, +#endif +#ifdef SSL_OP_PRIORITIZE_CHACHA + {"PRIORITIZE_CHACHA", SSL_OP_PRIORITIZE_CHACHA}, +#endif +#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG + {"SAFARI_ECDHE_ECDSA_BUG", SSL_OP_SAFARI_ECDHE_ECDSA_BUG}, +#endif +#ifdef SSL_OP_SINGLE_DH_USE + {"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE}, +#endif +#ifdef SSL_OP_SINGLE_ECDH_USE + {"SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE}, +#endif +#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG + {"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG}, +#endif +#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG + {"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG}, +#endif +#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG + {"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG}, +#endif +#ifdef SSL_OP_TLS_D5_BUG + {"TLS_D5_BUG", SSL_OP_TLS_D5_BUG}, +#endif +#ifdef SSL_OP_TLSEXT_PADDING + {"TLSEXT_PADDING", SSL_OP_TLSEXT_PADDING}, +#endif +#ifdef SSL_OP_TLSEXT_PADDING_SUPER + {"TLSEXT_PADDING_SUPER", SSL_OP_TLSEXT_PADDING_SUPER}, +#endif +#ifdef SSL_OP_TLS_ROLLBACK_BUG + {"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG}, +#endif + {NULL, 0} +}; + +NOEXPORT uint64_t parse_ssl_option(char *); +NOEXPORT void print_ssl_options(void); + +NOEXPORT SOCK_OPT *socket_options_init(void); +NOEXPORT void socket_option_set_int(SOCK_OPT *, const char *, int, int); +NOEXPORT SOCK_OPT *socket_options_dup(SOCK_OPT *); +NOEXPORT void socket_options_free(SOCK_OPT *); +NOEXPORT int socket_options_print(void); +NOEXPORT char *socket_option_text(VAL_TYPE, OPT_UNION *); +NOEXPORT int socket_option_parse(SOCK_OPT *, char *); + +#ifndef OPENSSL_NO_OCSP +NOEXPORT unsigned long parse_ocsp_flag(char *); +#endif /* !defined(OPENSSL_NO_OCSP) */ + +#ifndef OPENSSL_NO_ENGINE +NOEXPORT void engine_reset_list(void); +NOEXPORT const char *engine_auto(void); +NOEXPORT const char *engine_open(const char *); +NOEXPORT const char *engine_ctrl(const char *, const char *); +NOEXPORT const char *engine_default(const char *); +NOEXPORT const char *engine_init(void); +NOEXPORT ENGINE *engine_get_by_id(const char *); +NOEXPORT ENGINE *engine_get_by_num(const int); +#endif /* !defined(OPENSSL_NO_ENGINE) */ + +NOEXPORT const char *include_config(char *, SERVICE_OPTIONS **); + +NOEXPORT void print_syntax(void); + +NOEXPORT void name_list_append(NAME_LIST **, char *); +NOEXPORT void name_list_dup(NAME_LIST **, NAME_LIST *); +NOEXPORT void name_list_free(NAME_LIST *); +#ifndef USE_WIN32 +NOEXPORT char **arg_alloc(char *); +NOEXPORT char **arg_dup(char **); +NOEXPORT void arg_free(char **arg); +#endif + +static char *configuration_file=NULL; + +GLOBAL_OPTIONS global_options; +SERVICE_OPTIONS service_options; +unsigned number_of_sections=0; + +static GLOBAL_OPTIONS new_global_options; +static SERVICE_OPTIONS new_service_options; + +static const char *option_not_found= + "Specified option name is not valid here"; + +static const char *stunnel_cipher_list= + "HIGH:!aNULL:!SSLv2:!DH:!kDHEPSK"; + +#ifndef OPENSSL_NO_TLS1_3 +static const char *stunnel_ciphersuites= + "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"; +#endif /* TLS 1.3 */ + +/**************************************** parse commandline parameters */ + +/* return values: + 0 - configuration accepted + 1 - error + 2 - information printed +*/ + +int options_cmdline(char *arg1, char *arg2) { + const char *name; + CONF_TYPE type; + +#ifdef USE_WIN32 + (void)arg2; /* squash the unused parameter warning */ +#endif + if(!arg1) { + name= +#ifdef CONFDIR + CONFDIR +#ifdef USE_WIN32 + "\\" +#else + "/" +#endif +#endif + "stunnel.conf"; + type=CONF_FILE; + } else if(!strcasecmp(arg1, "-help")) { + parse_global_option(CMD_PRINT_HELP, NULL, NULL, NULL); + parse_service_option(CMD_PRINT_HELP, NULL, NULL, NULL); + log_flush(LOG_MODE_INFO); + return 2; + } else if(!strcasecmp(arg1, "-version")) { + parse_global_option(CMD_PRINT_DEFAULTS, NULL, NULL, NULL); + parse_service_option(CMD_PRINT_DEFAULTS, NULL, NULL, NULL); + log_flush(LOG_MODE_INFO); + return 2; + } else if(!strcasecmp(arg1, "-sockets")) { + socket_options_print(); + log_flush(LOG_MODE_INFO); + return 2; + } else if(!strcasecmp(arg1, "-options")) { + print_ssl_options(); + log_flush(LOG_MODE_INFO); + return 2; + } else +#ifndef USE_WIN32 + if(!strcasecmp(arg1, "-fd")) { + if(!arg2) { + s_log(LOG_ERR, "No file descriptor specified"); + print_syntax(); + return 1; + } + name=arg2; + type=CONF_FD; + } else +#endif + { + name=arg1; + type=CONF_FILE; + } + + if(type==CONF_FILE) { +#ifdef HAVE_REALPATH + char *buffer=NULL, *real_path; +#ifdef MAXPATHLEN + /* a workaround for pre-POSIX.1-2008 4.4BSD and Solaris */ + buffer=malloc(MAXPATHLEN); +#endif + real_path=realpath(name, buffer); + if(!real_path) { + free(buffer); + s_log(LOG_ERR, "Invalid configuration file name \"%s\"", name); + ioerror("realpath"); + return 1; + } + configuration_file=str_dup(real_path); + free(real_path); +#else + configuration_file=str_dup(name); +#endif +#ifndef USE_WIN32 + } else if(type==CONF_FD) { + configuration_file=str_dup(name); +#endif + } + return options_parse(type); +} + +/**************************************** parse configuration file */ + +int options_parse(CONF_TYPE type) { + SERVICE_OPTIONS *section; + + options_defaults(); + section=&new_service_options; + /* options_file() is a recursive function, so the last section of the + * configuration file section needs to be initialized separately */ + if(options_file(configuration_file, type, §ion) || + init_section(1, §ion)) { + s_log(LOG_ERR, "Configuration failed"); + options_free(0); /* free the new options */ + return 1; + } + s_log(LOG_NOTICE, "Configuration successful"); + return 0; +} + +NOEXPORT int options_file(char *path, CONF_TYPE type, + SERVICE_OPTIONS **section_ptr) { + DISK_FILE *df; + char line_text[CONFLINELEN]; + const char *errstr; + char config_line[CONFLINELEN], *config_opt, *config_arg; + int i, line_number=0; + const u_char utf8_bom[] = {0xef, 0xbb, 0xbf}; +#ifndef USE_WIN32 + int fd; + char *tmp_str; +#endif + + s_log(LOG_NOTICE, "Reading configuration from %s %s", + type==CONF_FD ? "descriptor" : "file", path); +#ifndef USE_WIN32 + if(type==CONF_FD) { /* file descriptor */ + fd=(int)strtol(path, &tmp_str, 10); + if(tmp_str==path || *tmp_str) { /* not a number */ + s_log(LOG_ERR, "Invalid file descriptor number"); + print_syntax(); + return 1; + } + df=file_fdopen(fd); + } else +#endif + df=file_open(path, FILE_MODE_READ); + if(!df) { + s_log(LOG_ERR, "Cannot open configuration file"); + if(type!=CONF_RELOAD) + print_syntax(); + return 1; + } + + while(file_getline(df, line_text, CONFLINELEN)>=0) { + memcpy(config_line, line_text, CONFLINELEN); + ++line_number; + config_opt=config_line; + if(line_number==1) { + if(!memcmp(config_opt, utf8_bom, sizeof utf8_bom)) { + s_log(LOG_NOTICE, "UTF-8 byte order mark detected"); + config_opt+=sizeof utf8_bom; + } else { + s_log(LOG_NOTICE, "UTF-8 byte order mark not detected"); + } + } + + while(isspace((unsigned char)*config_opt)) + ++config_opt; /* remove initial whitespaces */ + for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i) + config_opt[i]='\0'; /* remove trailing whitespaces */ + if(config_opt[0]=='\0' || config_opt[0]=='#' || config_opt[0]==';') /* empty or comment */ + continue; + + if(config_opt[0]=='[' && config_opt[strlen(config_opt)-1]==']') { /* new section */ + if(init_section(0, section_ptr)) { + file_close(df); + return 1; + } + + /* append a new SERVICE_OPTIONS structure to the list */ + { + SERVICE_OPTIONS *new_section; + new_section=str_alloc_detached(sizeof(SERVICE_OPTIONS)); + new_section->next=NULL; + (*section_ptr)->next=new_section; + *section_ptr=new_section; + } + + /* initialize the newly allocated section */ + ++config_opt; + config_opt[strlen(config_opt)-1]='\0'; + (*section_ptr)->servname=str_dup_detached(config_opt); + (*section_ptr)->session=NULL; + parse_service_option(CMD_SET_COPY, section_ptr, NULL, NULL); + continue; + } + + config_arg=strchr(config_line, '='); + if(!config_arg) { + s_log(LOG_ERR, "%s:%d: \"%s\": No '=' found", + path, line_number, line_text); + file_close(df); + return 1; + } + *config_arg++='\0'; /* split into option name and argument value */ + for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i) + config_opt[i]='\0'; /* remove trailing whitespaces */ + while(isspace((unsigned char)*config_arg)) + ++config_arg; /* remove initial whitespaces */ + + errstr=option_not_found; + /* try global options first (e.g. for 'debug') */ + if(!new_service_options.next) + errstr=parse_global_option(CMD_SET_VALUE, &new_global_options, config_opt, config_arg); + if(errstr==option_not_found) + errstr=parse_service_option(CMD_SET_VALUE, section_ptr, config_opt, config_arg); + if(errstr) { + s_log(LOG_ERR, "%s:%d: \"%s\": %s", + path, line_number, line_text, errstr); + file_close(df); + return 1; + } + } + file_close(df); + return 0; +} + +NOEXPORT int init_section(int eof, SERVICE_OPTIONS **section_ptr) { + const char *errstr; + +#ifndef USE_WIN32 + (*section_ptr)->option.log_stderr=new_global_options.option.log_stderr; +#endif /* USE_WIN32 */ + + if(*section_ptr==&new_service_options) { + /* end of global options or inetd mode -> initialize globals */ + errstr=parse_global_option(CMD_INITIALIZE, &new_global_options, NULL, NULL); + if(errstr) { + s_log(LOG_ERR, "Global options: %s", errstr); + return 1; + } + } + + if(*section_ptr!=&new_service_options || eof) { + /* end service section or inetd mode -> initialize service */ + errstr=parse_service_option(CMD_INITIALIZE, section_ptr, NULL, NULL); + if(errstr) { + if(*section_ptr==&new_service_options) + s_log(LOG_ERR, "Inetd mode: %s", errstr); + else + s_log(LOG_ERR, "Service [%s]: %s", + (*section_ptr)->servname, errstr); + return 1; + } + } + return 0; +} + +#ifdef USE_WIN32 + +int scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) { + WIN32_FIND_DATA data; + HANDLE h; + unsigned num=0, allocated=0; + LPTSTR path, pattern; + char *name; + DWORD saved_errno; + + (void)filter; /* squash the unused parameter warning */ + (void)compar; /* squash the unused parameter warning */ + path=str2tstr(dirp); + pattern=str_tprintf(TEXT("%s\\*"), path); + str_free(path); + h=FindFirstFile(pattern, &data); + saved_errno=GetLastError(); + str_free(pattern); + SetLastError(saved_errno); + if(h==INVALID_HANDLE_VALUE) + return -1; + *namelist=NULL; + do { + if(num>=allocated) { + allocated+=16; + *namelist=realloc(*namelist, allocated*sizeof(**namelist)); + } + (*namelist)[num]=malloc(sizeof(struct dirent)); + if(!(*namelist)[num]) + return -1; + name=tstr2str(data.cFileName); + strncpy((*namelist)[num]->d_name, name, MAX_PATH-1); + (*namelist)[num]->d_name[MAX_PATH-1]='\0'; + str_free(name); + ++num; + } while(FindNextFile(h, &data)); + FindClose(h); + return (int)num; +} + +int alphasort(const struct dirent **a, const struct dirent **b) { + (void)a; /* squash the unused parameter warning */ + (void)b; /* squash the unused parameter warning */ + /* most Windows filesystem return sorted data */ + return 0; +} + +#endif + +void options_defaults() { + SERVICE_OPTIONS *service; + + /* initialize globals *before* opening the config file */ + memset(&new_global_options, 0, sizeof(GLOBAL_OPTIONS)); + memset(&new_service_options, 0, sizeof(SERVICE_OPTIONS)); + new_service_options.next=NULL; + + parse_global_option(CMD_SET_DEFAULTS, &new_global_options, NULL, NULL); + service=&new_service_options; + parse_service_option(CMD_SET_DEFAULTS, &service, NULL, NULL); +} + +void options_apply() { /* apply default/validated configuration */ + unsigned num=0; + SERVICE_OPTIONS *section; + + CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_SECTIONS]); + + memcpy(&global_options, &new_global_options, sizeof(GLOBAL_OPTIONS)); + memset(&new_global_options, 0, sizeof(GLOBAL_OPTIONS)); + + /* service_options are used for inetd mode and to enumerate services */ + for(section=new_service_options.next; section; section=section->next) + section->section_number=num++; + memcpy(&service_options, &new_service_options, sizeof(SERVICE_OPTIONS)); + memset(&new_service_options, 0, sizeof(SERVICE_OPTIONS)); + number_of_sections=num; + + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_SECTIONS]); +} + +void options_free(int current) { + GLOBAL_OPTIONS *global=current?&global_options:&new_global_options; + SERVICE_OPTIONS *service=current?&service_options:&new_service_options; + + parse_global_option(CMD_FREE, global, NULL, NULL); + + CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_SECTIONS]); + while(service) { + SERVICE_OPTIONS *tmp=service; + service=service->next; + tmp->next=NULL; + service_free(tmp); + } + CRYPTO_THREAD_unlock(stunnel_locks[LOCK_SECTIONS]); +} + +void service_up_ref(SERVICE_OPTIONS *section) { +#ifdef USE_OS_THREADS + int ref; + + CRYPTO_atomic_add(§ion->ref, 1, &ref, stunnel_locks[LOCK_REF]); +#else + ++(section->ref); +#endif +} + +void service_free(SERVICE_OPTIONS *section) { + int ref; + +#ifdef USE_OS_THREADS + CRYPTO_atomic_add(§ion->ref, -1, &ref, stunnel_locks[LOCK_REF]); +#else + ref=--(section->ref); +#endif + if(ref<0) + fatal("Negative section reference counter"); + if(ref==0) + parse_service_option(CMD_FREE, §ion, NULL, NULL); +} + +/**************************************** global options */ + +NOEXPORT const char *parse_global_option(CMD cmd, GLOBAL_OPTIONS *options, char *opt, char *arg) { + void *tmp; + + if(cmd==CMD_PRINT_DEFAULTS || cmd==CMD_PRINT_HELP) { + s_log(LOG_NOTICE, " "); + s_log(LOG_NOTICE, "Global options:"); + } + + /* chroot */ +#ifdef HAVE_CHROOT + switch(cmd) { + case CMD_SET_DEFAULTS: + options->chroot_dir=NULL; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + tmp=options->chroot_dir; + options->chroot_dir=NULL; + str_free(tmp); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "chroot")) + break; + options->chroot_dir=str_dup(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = directory to chroot stunnel process", "chroot"); + break; + } +#endif /* HAVE_CHROOT */ + + /* compression */ +#ifndef OPENSSL_NO_COMP + switch(cmd) { + case CMD_SET_DEFAULTS: + options->compression=COMP_NONE; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "compression")) + break; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* only allow compression with OpenSSL 0.9.8 or later + * with OpenSSL #1468 zlib memory leak fixed */ + if(OpenSSL_version_num()<0x00908051L) /* 0.9.8e-beta1 */ + return "Compression unsupported due to a memory leak"; +#endif /* OpenSSL version < 1.1.0 */ + if(!strcasecmp(arg, "deflate")) + options->compression=COMP_DEFLATE; + else if(!strcasecmp(arg, "zlib")) + options->compression=COMP_ZLIB; + else + return "Specified compression type is not available"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = compression type", + "compression"); + break; + } +#endif /* !defined(OPENSSL_NO_COMP) */ + + /* EGD */ + switch(cmd) { + case CMD_SET_DEFAULTS: +#ifdef EGD_SOCKET + options->egd_sock=EGD_SOCKET; +#else + options->egd_sock=NULL; +#endif + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + tmp=options->egd_sock; + options->egd_sock=NULL; + str_free(tmp); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "EGD")) + break; + options->egd_sock=str_dup(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#ifdef EGD_SOCKET + s_log(LOG_NOTICE, "%-22s = %s", "EGD", EGD_SOCKET); +#endif + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = path to Entropy Gathering Daemon socket", "EGD"); + break; + } + +#ifndef OPENSSL_NO_ENGINE + + /* engine */ + switch(cmd) { + case CMD_SET_DEFAULTS: + engine_reset_list(); + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + /* FIXME: investigate if we can free it */ + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "engine")) + break; + if(!strcasecmp(arg, "auto")) + return engine_auto(); + else + return engine_open(arg); + case CMD_INITIALIZE: + engine_init(); + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = auto|engine_id", + "engine"); + break; + } + + /* engineCtrl */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "engineCtrl")) + break; + { + char *tmp_str=strchr(arg, ':'); + if(tmp_str) + *tmp_str++='\0'; + return engine_ctrl(arg, tmp_str); + } + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = cmd[:arg]", + "engineCtrl"); + break; + } + + /* engineDefault */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "engineDefault")) + break; + return engine_default(arg); + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = TASK_LIST", + "engineDefault"); + break; + } + +#endif /* !defined(OPENSSL_NO_ENGINE) */ + + /* fips */ + switch(cmd) { + case CMD_SET_DEFAULTS: +#ifdef USE_FIPS + options->option.fips=fips_default()?1:0; +#endif /* USE_FIPS */ + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "fips")) + break; + if(!strcasecmp(arg, "yes")) { +#ifdef USE_FIPS + options->option.fips=1; +#else + return "FIPS support is not available"; +#endif /* USE_FIPS */ + } else if(!strcasecmp(arg, "no")) { +#ifdef USE_FIPS + if(fips_default()) + return "Failed to override system-wide FIPS mode"; + options->option.fips=0; +#endif /* USE_FIPS */ + } else { + return "The argument needs to be either 'yes' or 'no'"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#ifdef USE_FIPS + if(fips_available()) + s_log(LOG_NOTICE, "%-22s = %s", "fips", fips_default()?"yes":"no"); +#endif /* USE_FIPS */ + break; + case CMD_PRINT_HELP: + if(fips_available()) + s_log(LOG_NOTICE, "%-22s = yes|no FIPS 140-2 mode", + "fips"); + break; + } + + /* foreground */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + options->option.foreground=0; + options->option.log_stderr=0; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "foreground")) + break; + if(!strcasecmp(arg, "yes")) { + options->option.foreground=1; + options->option.log_stderr=1; + } else if(!strcasecmp(arg, "quiet")) { + options->option.foreground=1; + options->option.log_stderr=0; + } else if(!strcasecmp(arg, "no")) { + options->option.foreground=0; + options->option.log_stderr=0; + } else + return "The argument needs to be either 'yes', 'quiet' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|quiet|no foreground mode (don't fork, log to stderr)", + "foreground"); + break; + } +#endif + +#ifdef ICON_IMAGE + + /* iconActive */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->icon[ICON_ACTIVE]=load_icon_default(ICON_ACTIVE); + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + /* FIXME: investigate if we can free it */ + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "iconActive")) + break; + options->icon[ICON_ACTIVE]=load_icon_file(arg); + if(!options->icon[ICON_ACTIVE]) + return "Failed to load the specified icon"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = icon when connections are established", "iconActive"); + break; + } + + /* iconError */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->icon[ICON_ERROR]=load_icon_default(ICON_ERROR); + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + /* FIXME: investigate if we can free it */ + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "iconError")) + break; + options->icon[ICON_ERROR]=load_icon_file(arg); + if(!options->icon[ICON_ERROR]) + return "Failed to load the specified icon"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = icon for invalid configuration file", "iconError"); + break; + } + + /* iconIdle */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->icon[ICON_IDLE]=load_icon_default(ICON_IDLE); + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + /* FIXME: investigate if we can free it */ + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "iconIdle")) + break; + options->icon[ICON_IDLE]=load_icon_file(arg); + if(!options->icon[ICON_IDLE]) + return "Failed to load the specified icon"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = icon when no connections were established", "iconIdle"); + break; + } + +#endif /* ICON_IMAGE */ + + /* log */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->log_file_mode=FILE_MODE_APPEND; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "log")) + break; + if(!strcasecmp(arg, "append")) + options->log_file_mode=FILE_MODE_APPEND; + else if(!strcasecmp(arg, "overwrite")) + options->log_file_mode=FILE_MODE_OVERWRITE; + else + return "The argument needs to be either 'append' or 'overwrite'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = append|overwrite log file", + "log"); + break; + } + + /* output */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->output_file=NULL; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + tmp=options->output_file; + options->output_file=NULL; + str_free(tmp); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "output")) + break; + options->output_file=str_dup(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: +#ifndef USE_WIN32 + if(!options->option.foreground /* daemonize() used */ && + options->output_file /* log file enabled */ && + options->output_file[0]!='/' /* relative path */) + return "Log file must include full path name"; +#endif + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = file to append log messages", "output"); + break; + } + + /* pid */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + options->pidfile=NULL; /* do not create a pid file */ + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + tmp=options->pidfile; + options->pidfile=NULL; + str_free(tmp); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "pid")) + break; + if(arg[0]) /* is argument not empty? */ + options->pidfile=str_dup(arg); + else + options->pidfile=NULL; /* empty -> do not create a pid file */ + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!options->option.foreground /* daemonize() used */ && + options->pidfile /* pid file enabled */ && + options->pidfile[0]!='/' /* relative path */) + return "Pid file must include full path name"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = pid file", "pid"); + break; + } +#endif + + /* RNDbytes */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->random_bytes=RANDOM_BYTES; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "RNDbytes")) + break; + { + char *tmp_str; + options->random_bytes=(long)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal number of bytes to read from random seed files"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d", "RNDbytes", RANDOM_BYTES); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = bytes to read from random seed files", "RNDbytes"); + break; + } + + /* RNDfile */ + switch(cmd) { + case CMD_SET_DEFAULTS: +#ifdef RANDOM_FILE + options->rand_file=str_dup(RANDOM_FILE); +#else + options->rand_file=NULL; +#endif + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + tmp=options->rand_file; + options->rand_file=NULL; + str_free(tmp); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "RNDfile")) + break; + options->rand_file=str_dup(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#ifdef RANDOM_FILE + s_log(LOG_NOTICE, "%-22s = %s", "RNDfile", RANDOM_FILE); +#endif + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = path to file with random seed data", "RNDfile"); + break; + } + + /* RNDoverwrite */ + switch(cmd) { + case CMD_SET_DEFAULTS: + options->option.rand_write=1; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "RNDoverwrite")) + break; + if(!strcasecmp(arg, "yes")) + options->option.rand_write=1; + else if(!strcasecmp(arg, "no")) + options->option.rand_write=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = yes", "RNDoverwrite"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no overwrite seed datafiles with new random data", + "RNDoverwrite"); + break; + } + + /* syslog */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + options->option.log_syslog=1; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "syslog")) + break; + if(!strcasecmp(arg, "yes")) + options->option.log_syslog=1; + else if(!strcasecmp(arg, "no")) + options->option.log_syslog=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no send logging messages to syslog", + "syslog"); + break; + } +#endif + + /* taskbar */ +#ifdef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + options->option.taskbar=1; + break; + case CMD_SET_COPY: /* not used for global options */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "taskbar")) + break; + if(!strcasecmp(arg, "yes")) + options->option.taskbar=1; + else if(!strcasecmp(arg, "no")) + options->option.taskbar=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = yes", "taskbar"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no enable the taskbar icon", "taskbar"); + break; + } +#endif + + /* final checks */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: + break; + case CMD_FREE: + memset(options, 0, sizeof(GLOBAL_OPTIONS)); + break; + case CMD_SET_VALUE: + return option_not_found; + case CMD_INITIALIZE: + /* FIPS needs to be initialized as early as possible */ + if(ssl_configure(options)) /* configure global TLS settings */ + return "Failed to initialize TLS"; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + break; + } + return NULL; /* OK */ +} + +/**************************************** service-level options */ + +NOEXPORT const char *parse_service_option(CMD cmd, SERVICE_OPTIONS **section_ptr, + char *opt, char *arg) { + SERVICE_OPTIONS *section; + int endpoints=0; +#ifndef USE_WIN32 + struct group *gr; + struct passwd *pw; +#endif + + section=section_ptr ? *section_ptr : NULL; + + if(cmd==CMD_SET_DEFAULTS || cmd==CMD_SET_COPY) { + section->ref=1; + if(section==&service_options) + s_log(LOG_ERR, "INTERNAL ERROR: Initializing deployed section defaults"); + else if(section==&new_service_options) + s_log(LOG_INFO, "Initializing inetd mode configuration"); + else + s_log(LOG_INFO, "Initializing service [%s]", section->servname); + } else if(cmd==CMD_FREE) { + if(section==&service_options) + s_log(LOG_DEBUG, "Deallocating deployed section defaults"); + else if(section==&new_service_options) + s_log(LOG_DEBUG, "Deallocating temporary section defaults"); + else + s_log(LOG_DEBUG, "Deallocating section [%s]", section->servname); + } else if(cmd==CMD_PRINT_DEFAULTS || cmd==CMD_PRINT_HELP) { + s_log(LOG_NOTICE, " "); + s_log(LOG_NOTICE, "Service-level options:"); + } + + /* accept */ + switch(cmd) { + case CMD_SET_DEFAULTS: + addrlist_clear(§ion->local_addr, 1); + section->local_fd=NULL; + break; + case CMD_SET_COPY: + addrlist_clear(§ion->local_addr, 1); + section->local_fd=NULL; + name_list_dup(§ion->local_addr.names, + new_service_options.local_addr.names); + break; + case CMD_FREE: + name_list_free(section->local_addr.names); + str_free(section->local_addr.addr); + str_free(section->local_fd); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "accept")) + break; + section->option.accept=1; + name_list_append(§ion->local_addr.names, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->local_addr.names) { + unsigned i; + if(!addrlist_resolve(§ion->local_addr)) + return "Cannot resolve accept target"; + section->local_fd=str_alloc_detached(section->local_addr.num*sizeof(SOCKET)); + for(i=0; ilocal_addr.num; ++i) + section->local_fd[i]=INVALID_SOCKET; + ++endpoints; + } + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = [host:]port accept connections on specified host:port", + "accept"); + break; + } + + /* CApath */ + switch(cmd) { + case CMD_SET_DEFAULTS: +#if 0 + section->ca_dir=(char *)X509_get_default_cert_dir(); +#endif + section->ca_dir=NULL; + break; + case CMD_SET_COPY: + section->ca_dir=str_dup_detached(new_service_options.ca_dir); + break; + case CMD_FREE: + str_free(section->ca_dir); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "CApath")) + break; + str_free(section->ca_dir); + if(arg[0]) /* not empty */ + section->ca_dir=str_dup_detached(arg); + else + section->ca_dir=NULL; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#if 0 + s_log(LOG_NOTICE, "%-22s = %s", "CApath", + section->ca_dir ? section->ca_dir : "(none)"); +#endif + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = CA certificate directory for 'verify' option", + "CApath"); + break; + } + + /* CAfile */ + switch(cmd) { + case CMD_SET_DEFAULTS: +#if 0 + section->ca_file=(char *)X509_get_default_certfile(); +#endif + section->ca_file=NULL; + break; + case CMD_SET_COPY: + section->ca_file=str_dup_detached(new_service_options.ca_file); + break; + case CMD_FREE: + str_free(section->ca_file); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "CAfile")) + break; + str_free(section->ca_file); + if(arg[0]) /* not empty */ + section->ca_file=str_dup_detached(arg); + else + section->ca_file=NULL; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#if 0 + s_log(LOG_NOTICE, "%-22s = %s", "CAfile", + section->ca_file ? section->ca_file : "(none)"); +#endif + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = CA certificate file for 'verify' option", + "CAfile"); + break; + } + + /* cert */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->cert=NULL; + break; + case CMD_SET_COPY: + section->cert=str_dup_detached(new_service_options.cert); + break; + case CMD_FREE: + str_free(section->cert); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "cert")) + break; + str_free(section->cert); + section->cert=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: +#ifndef OPENSSL_NO_PSK + if(section->psk_keys) + break; +#endif /* !defined(OPENSSL_NO_PSK) */ +#ifndef OPENSSL_NO_ENGINE + if(section->engine) + break; +#endif /* !defined(OPENSSL_NO_ENGINE) */ + if(!section->option.client && !section->cert) + return "TLS server needs a certificate"; + break; + case CMD_PRINT_DEFAULTS: + break; /* no default certificate */ + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = certificate chain", "cert"); + break; + } + +#if OPENSSL_VERSION_NUMBER>=0x10002000L + + /* checkEmail */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->check_email=NULL; + break; + case CMD_SET_COPY: + name_list_dup(§ion->check_email, + new_service_options.check_email); + break; + case CMD_FREE: + name_list_free(section->check_email); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "checkEmail")) + break; + name_list_append(§ion->check_email, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->check_email && !section->option.verify_chain && !section->option.verify_peer) + return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = peer certificate email address", + "checkEmail"); + break; + } + + /* checkHost */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->check_host=NULL; + break; + case CMD_SET_COPY: + name_list_dup(§ion->check_host, + new_service_options.check_host); + break; + case CMD_FREE: + name_list_free(section->check_host); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "checkHost")) + break; + name_list_append(§ion->check_host, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->check_host && !section->option.verify_chain && !section->option.verify_peer) + return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = peer certificate host name pattern", + "checkHost"); + break; + } + + /* checkIP */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->check_ip=NULL; + break; + case CMD_SET_COPY: + name_list_dup(§ion->check_ip, + new_service_options.check_ip); + break; + case CMD_FREE: + name_list_free(section->check_ip); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "checkIP")) + break; + name_list_append(§ion->check_ip, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->check_ip && !section->option.verify_chain && !section->option.verify_peer) + return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = peer certificate IP address", + "checkIP"); + break; + } + +#endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */ + + /* ciphers */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->cipher_list=NULL; + break; + case CMD_SET_COPY: + section->cipher_list=str_dup_detached(new_service_options.cipher_list); + break; + case CMD_FREE: + str_free(section->cipher_list); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ciphers")) + break; + str_free(section->cipher_list); + section->cipher_list=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!section->cipher_list) { + /* this is only executed for global options, because + * section->cipher_list is no longer NULL in sections */ +#ifdef USE_FIPS + if(new_global_options.option.fips) + section->cipher_list=str_dup_detached("FIPS"); + else +#endif /* USE_FIPS */ + section->cipher_list=str_dup_detached(stunnel_cipher_list); + } + break; + case CMD_PRINT_DEFAULTS: + if(fips_available()) { + s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers", + "FIPS", "(with \"fips = yes\")"); + s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers", + stunnel_cipher_list, "(with \"fips = no\")"); + } else { + s_log(LOG_NOTICE, "%-22s = %s", "ciphers", stunnel_cipher_list); + } + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = permitted ciphers for TLS 1.2 or older", "ciphers"); + break; + } + +#ifndef OPENSSL_NO_TLS1_3 + /* ciphersuites */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ciphersuites=NULL; + break; + case CMD_SET_COPY: + section->ciphersuites=str_dup_detached(new_service_options.ciphersuites); + break; + case CMD_FREE: + str_free(section->ciphersuites); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ciphersuites")) + break; + str_free(section->ciphersuites); + section->ciphersuites=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!section->ciphersuites) { + /* this is only executed for global options, because + * section->ciphersuites is no longer NULL in sections */ + section->ciphersuites=str_dup_detached(stunnel_ciphersuites); + } + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %s %s", "ciphersuites", stunnel_ciphersuites, "(with TLSv1.3)"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = permitted ciphersuites for TLS 1.3", "ciphersuites"); + break; + } +#endif /* TLS 1.3 */ + + /* client */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.client=0; + break; + case CMD_SET_COPY: + section->option.client=new_service_options.option.client; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "client")) + break; + if(!strcasecmp(arg, "yes")) + section->option.client=1; + else if(!strcasecmp(arg, "no")) + section->option.client=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no client mode (remote service uses TLS)", + "client"); + break; + } + +#if OPENSSL_VERSION_NUMBER>=0x10002000L + + /* config */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->config=NULL; + break; + case CMD_SET_COPY: + name_list_dup(§ion->config, new_service_options.config); + break; + case CMD_FREE: + name_list_free(section->config); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "config")) + break; + name_list_append(§ion->config, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = command[:parameter] to execute", + "config"); + break; + } + +#endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */ + + /* connect */ + switch(cmd) { + case CMD_SET_DEFAULTS: + addrlist_clear(§ion->connect_addr, 0); + section->connect_session=NULL; + break; + case CMD_SET_COPY: + addrlist_clear(§ion->connect_addr, 0); + section->connect_session=NULL; + name_list_dup(§ion->connect_addr.names, + new_service_options.connect_addr.names); + break; + case CMD_FREE: + name_list_free(section->connect_addr.names); + str_free(section->connect_addr.addr); + str_free(section->connect_session); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "connect")) + break; + name_list_append(§ion->connect_addr.names, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->connect_addr.names) { + if(!section->option.delayed_lookup && + !addrlist_resolve(§ion->connect_addr)) { + s_log(LOG_INFO, + "Cannot resolve connect target - delaying DNS lookup"); + section->connect_addr.num=0; + section->redirect_addr.num=0; + section->option.delayed_lookup=1; + } + if(section->option.client) + section->connect_session= + str_alloc_detached(section->connect_addr.num*sizeof(SSL_SESSION *)); + ++endpoints; + } + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = [host:]port to connect", + "connect"); + break; + } + + /* CRLpath */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->crl_dir=NULL; + break; + case CMD_SET_COPY: + section->crl_dir=str_dup_detached(new_service_options.crl_dir); + break; + case CMD_FREE: + str_free(section->crl_dir); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "CRLpath")) + break; + str_free(section->crl_dir); + if(arg[0]) /* not empty */ + section->crl_dir=str_dup_detached(arg); + else + section->crl_dir=NULL; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = CRL directory", "CRLpath"); + break; + } + + /* CRLfile */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->crl_file=NULL; + break; + case CMD_SET_COPY: + section->crl_file=str_dup_detached(new_service_options.crl_file); + break; + case CMD_FREE: + str_free(section->crl_file); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "CRLfile")) + break; + str_free(section->crl_file); + if(arg[0]) /* not empty */ + section->crl_file=str_dup_detached(arg); + else + section->crl_file=NULL; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = CRL file", "CRLfile"); + break; + } + +#ifndef OPENSSL_NO_ECDH + + /* curves */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->curves=str_dup_detached(DEFAULT_CURVES); + break; + case CMD_SET_COPY: + section->curves=str_dup_detached(new_service_options.curves); + break; + case CMD_FREE: + str_free(section->curves); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "curves") && strcasecmp(opt, "curve")) + break; + str_free(section->curves); + section->curves=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %s", "curves", DEFAULT_CURVES); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = ECDH curve names", "curves"); + break; + } + +#endif /* !defined(OPENSSL_NO_ECDH) */ + + /* debug */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->log_level=LOG_NOTICE; +#if !defined (USE_WIN32) && !defined (__vms) + new_global_options.log_facility=LOG_DAEMON; +#endif + break; + case CMD_SET_COPY: + section->log_level=new_service_options.log_level; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "debug")) + break; + return parse_debug_level(arg, section); + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: +#if !defined (USE_WIN32) && !defined (__vms) + s_log(LOG_NOTICE, "%-22s = %s", "debug", "daemon.notice"); +#else + s_log(LOG_NOTICE, "%-22s = %s", "debug", "notice"); +#endif + break; + case CMD_PRINT_HELP: +#if !defined (USE_WIN32) && !defined (__vms) + s_log(LOG_NOTICE, "%-22s = [facility].level (e.g. daemon.info)", "debug"); +#else + s_log(LOG_NOTICE, "%-22s = level (e.g. info)", "debug"); +#endif + break; + } + + /* delay */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.delayed_lookup=0; + break; + case CMD_SET_COPY: + section->option.delayed_lookup=new_service_options.option.delayed_lookup; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "delay")) + break; + if(!strcasecmp(arg, "yes")) + section->option.delayed_lookup=1; + else if(!strcasecmp(arg, "no")) + section->option.delayed_lookup=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = yes|no delay DNS lookup for 'connect' option", + "delay"); + break; + } + +#ifndef OPENSSL_NO_ENGINE + + /* engineId */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: + section->engine=new_service_options.engine; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "engineId")) + break; + section->engine=engine_get_by_id(arg); + if(!section->engine) + return "Engine ID not found"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = ID of engine to read the key from", + "engineId"); + break; + } + + /* engineNum */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: + section->engine=new_service_options.engine; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "engineNum")) + break; + { + char *tmp_str; + int tmp_int=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal engine number"; + section->engine=engine_get_by_num(tmp_int-1); + } + if(!section->engine) + return "Illegal engine number"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = number of engine to read the key from", + "engineNum"); + break; + } + +#endif /* !defined(OPENSSL_NO_ENGINE) */ + + /* exec */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->exec_name=NULL; + break; + case CMD_SET_COPY: + section->exec_name=str_dup_detached(new_service_options.exec_name); + break; + case CMD_FREE: + str_free(section->exec_name); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "exec")) + break; + str_free(section->exec_name); + section->exec_name=str_dup_detached(arg); +#ifdef USE_WIN32 + section->exec_args=str_dup_detached(arg); +#else + if(!section->exec_args) { + section->exec_args=str_alloc_detached(2*sizeof(char *)); + section->exec_args[0]=str_dup_detached(section->exec_name); + section->exec_args[1]=NULL; /* null-terminate */ + } +#endif + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->exec_name) + ++endpoints; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = file execute local inetd-type program", + "exec"); + break; + } + + /* execArgs */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->exec_args=NULL; + break; + case CMD_SET_COPY: +#ifdef USE_WIN32 + section->exec_args=str_dup_detached(new_service_options.exec_args); +#else + section->exec_args=arg_dup(new_service_options.exec_args); +#endif + break; + case CMD_FREE: +#ifdef USE_WIN32 + str_free(section->exec_args); +#else + arg_free(section->exec_args); +#endif + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "execArgs")) + break; +#ifdef USE_WIN32 + str_free(section->exec_args); + section->exec_args=str_dup_detached(arg); +#else + arg_free(section->exec_args); + section->exec_args=arg_alloc(arg); +#endif + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = arguments for 'exec' (including $0)", + "execArgs"); + break; + } + + /* failover */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->failover=FAILOVER_PRIO; + section->rr=0; + break; + case CMD_SET_COPY: + section->failover=new_service_options.failover; + section->rr=new_service_options.rr; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "failover")) + break; + if(!strcasecmp(arg, "rr")) + section->failover=FAILOVER_RR; + else if(!strcasecmp(arg, "prio")) + section->failover=FAILOVER_PRIO; + else + return "The argument needs to be either 'rr' or 'prio'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->option.delayed_lookup) + section->failover=FAILOVER_PRIO; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = rr|prio failover strategy", + "failover"); + break; + } + + /* ident */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->username=NULL; + break; + case CMD_SET_COPY: + section->username=str_dup_detached(new_service_options.username); + break; + case CMD_FREE: + str_free(section->username); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ident")) + break; + str_free(section->username); + section->username=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = username for IDENT (RFC 1413) checking", "ident"); + break; + } + + /* include */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "include")) + break; + return include_config(arg, section_ptr); + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = directory with configuration file snippets", + "include"); + break; + } + + /* key */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->key=NULL; + break; + case CMD_SET_COPY: + section->key=str_dup_detached(new_service_options.key); + break; + case CMD_FREE: + str_free(section->key); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "key")) + break; + str_free(section->key); + section->key=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->cert && !section->key) + section->key=str_dup_detached(section->cert); + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = certificate private key", "key"); + break; + } + + /* libwrap */ +#ifdef USE_LIBWRAP + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.libwrap=0; /* disable libwrap by default */ + break; + case CMD_SET_COPY: + section->option.libwrap=new_service_options.option.libwrap; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "libwrap")) + break; + if(!strcasecmp(arg, "yes")) + section->option.libwrap=1; + else if(!strcasecmp(arg, "no")) + section->option.libwrap=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no use /etc/hosts.allow and /etc/hosts.deny", + "libwrap"); + break; + } +#endif /* USE_LIBWRAP */ + + /* local */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.local=0; + break; + case CMD_SET_COPY: + section->option.local=new_service_options.option.local; + memcpy(§ion->source_addr, &new_service_options.source_addr, + sizeof(SOCKADDR_UNION)); + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "local")) + break; + if(!hostport2addr(§ion->source_addr, arg, "0", 1)) + return "Failed to resolve local address"; + section->option.local=1; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = IP address to be used as source for remote" + " connections", "local"); + break; + } + + /* logId */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->log_id=LOG_ID_SEQUENTIAL; + break; + case CMD_SET_COPY: + section->log_id=new_service_options.log_id; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "logId")) + break; + if(!strcasecmp(arg, "sequential")) + section->log_id=LOG_ID_SEQUENTIAL; + else if(!strcasecmp(arg, "unique")) + section->log_id=LOG_ID_UNIQUE; + else if(!strcasecmp(arg, "thread")) + section->log_id=LOG_ID_THREAD; + else if(!strcasecmp(arg, "process")) + section->log_id=LOG_ID_PROCESS; + else + return "Invalid connection identifier type"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %s", "logId", "sequential"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = connection identifier type", + "logId"); + break; + } + +#ifndef OPENSSL_NO_OCSP + + /* OCSP */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ocsp_url=NULL; + break; + case CMD_SET_COPY: + section->ocsp_url=str_dup_detached(new_service_options.ocsp_url); + break; + case CMD_FREE: + str_free(section->ocsp_url); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ocsp")) + break; + str_free(section->ocsp_url); + section->ocsp_url=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = OCSP responder URL", "OCSP"); + break; + } + + /* OCSPaia */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.aia=0; /* disable AIA by default */ + break; + case CMD_SET_COPY: + section->option.aia=new_service_options.option.aia; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "OCSPaia")) + break; + if(!strcasecmp(arg, "yes")) + section->option.aia=1; + else if(!strcasecmp(arg, "no")) + section->option.aia=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = yes|no check the AIA responders from certificates", + "OCSPaia"); + break; + } + + /* OCSPflag */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ocsp_flags=0; + break; + case CMD_SET_COPY: + section->ocsp_flags=new_service_options.ocsp_flags; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "OCSPflag")) + break; + { + unsigned long tmp_ulong=parse_ocsp_flag(arg); + if(!tmp_ulong) + return "Illegal OCSP flag"; + section->ocsp_flags|=tmp_ulong; + } + return NULL; + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = OCSP responder flags", "OCSPflag"); + break; + } + + /* OCSPnonce */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.nonce=0; /* disable OCSP nonce by default */ + break; + case CMD_SET_COPY: + section->option.nonce=new_service_options.option.nonce; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "OCSPnonce")) + break; + if(!strcasecmp(arg, "yes")) + section->option.nonce=1; + else if(!strcasecmp(arg, "no")) + section->option.nonce=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = yes|no send and verify the OCSP nonce extension", + "OCSPnonce"); + break; + } + +#endif /* !defined(OPENSSL_NO_OCSP) */ + + /* options */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ssl_options_set=0; +#if OPENSSL_VERSION_NUMBER>=0x009080dfL + section->ssl_options_clear=0; +#endif /* OpenSSL 0.9.8m or later */ + break; + case CMD_SET_COPY: + section->ssl_options_set=new_service_options.ssl_options_set; +#if OPENSSL_VERSION_NUMBER>=0x009080dfL + section->ssl_options_clear=new_service_options.ssl_options_clear; +#endif /* OpenSSL 0.9.8m or later */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "options")) + break; +#if OPENSSL_VERSION_NUMBER>=0x009080dfL + if(*arg=='-') { + uint64_t tmp=parse_ssl_option(arg+1); + if(tmp==INVALID_SSL_OPTION) + return "Illegal TLS option"; + section->ssl_options_clear|=tmp; + return NULL; /* OK */ + } +#endif /* OpenSSL 0.9.8m or later */ + { + uint64_t tmp=parse_ssl_option(arg); + if(tmp==INVALID_SSL_OPTION) + return "Illegal TLS option"; + section->ssl_options_set|=tmp; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv2"); + s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv3"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = TLS option to set/reset", "options"); + break; + } + + /* protocol */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol=NULL; + break; + case CMD_SET_COPY: + section->protocol=str_dup_detached(new_service_options.protocol); + break; + case CMD_FREE: + str_free(section->protocol); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocol")) + break; + str_free(section->protocol); + section->protocol=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + /* PROTOCOL_CHECK also initializes: + section->option.connect_before_ssl + section->option.protocol_endpoint */ + { + const char *tmp_str=protocol(NULL, section, PROTOCOL_CHECK); + if(tmp_str) + return tmp_str; + } + endpoints+=section->option.protocol_endpoint; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = protocol to negotiate before TLS initialization", + "protocol"); + s_log(LOG_NOTICE, "%25scurrently supported: cifs, connect, imap,", ""); + s_log(LOG_NOTICE, "%25s nntp, pgsql, pop3, proxy, smtp, socks", ""); + break; + } + + /* protocolAuthentication */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_authentication=str_dup_detached("basic"); + break; + case CMD_SET_COPY: + section->protocol_authentication= + str_dup_detached(new_service_options.protocol_authentication); + break; + case CMD_FREE: + str_free(section->protocol_authentication); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolAuthentication")) + break; + str_free(section->protocol_authentication); + section->protocol_authentication=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = authentication type for protocol negotiations", + "protocolAuthentication"); + break; + } + + /* protocolDomain */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_domain=NULL; + break; + case CMD_SET_COPY: + section->protocol_domain= + str_dup_detached(new_service_options.protocol_domain); + break; + case CMD_FREE: + str_free(section->protocol_domain); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolDomain")) + break; + str_free(section->protocol_domain); + section->protocol_domain=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = domain for protocol negotiations", + "protocolDomain"); + break; + } + + /* protocolHeader */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_header=NULL; + break; + case CMD_SET_COPY: + name_list_dup(§ion->protocol_header, + new_service_options.protocol_header); + break; + case CMD_FREE: + name_list_free(section->protocol_header); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolHeader")) + break; + name_list_append(§ion->protocol_header, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = custom header for protocol negotiations", + "protocolHeader"); + break; + } + + /* protocolHost */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_host=NULL; + break; + case CMD_SET_COPY: + section->protocol_host= + str_dup_detached(new_service_options.protocol_host); + break; + case CMD_FREE: + str_free(section->protocol_host); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolHost")) + break; + str_free(section->protocol_host); + section->protocol_host=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = host:port for protocol negotiations", + "protocolHost"); + break; + } + + /* protocolPassword */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_password=NULL; + break; + case CMD_SET_COPY: + section->protocol_password= + str_dup_detached(new_service_options.protocol_password); + break; + case CMD_FREE: + str_free(section->protocol_password); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolPassword")) + break; + str_free(section->protocol_password); + section->protocol_password=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = password for protocol negotiations", + "protocolPassword"); + break; + } + + /* protocolUsername */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->protocol_username=NULL; + break; + case CMD_SET_COPY: + section->protocol_username= + str_dup_detached(new_service_options.protocol_username); + break; + case CMD_FREE: + str_free(section->protocol_username); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "protocolUsername")) + break; + str_free(section->protocol_username); + section->protocol_username=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = username for protocol negotiations", + "protocolUsername"); + break; + } + +#ifndef OPENSSL_NO_PSK + + /* PSKidentity */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->psk_identity=NULL; + section->psk_selected=NULL; + section->psk_sorted.val=NULL; + section->psk_sorted.num=0; + break; + case CMD_SET_COPY: + section->psk_identity= + str_dup_detached(new_service_options.psk_identity); + break; + case CMD_FREE: + str_free(section->psk_identity); + str_free(section->psk_sorted.val); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "PSKidentity")) + break; + str_free(section->psk_identity); + section->psk_identity=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!section->psk_keys) /* PSK not configured */ + break; + psk_sort(§ion->psk_sorted, section->psk_keys); + if(section->option.client) { + if(section->psk_identity) { + section->psk_selected= + psk_find(§ion->psk_sorted, section->psk_identity); + if(!section->psk_selected) + return "No key found for the specified PSK identity"; + } else { /* take the first specified identity as default */ + section->psk_selected=section->psk_keys; + } + } else { + if(section->psk_identity) + s_log(LOG_NOTICE, + "PSK identity is ignored in the server mode"); + } + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = identity for PSK authentication", + "PSKidentity"); + break; + } + + /* PSKsecrets */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->psk_keys=NULL; + break; + case CMD_SET_COPY: + section->psk_keys=psk_dup(new_service_options.psk_keys); + break; + case CMD_FREE: + psk_free(section->psk_keys); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "PSKsecrets")) + break; + section->psk_keys=psk_read(arg); + if(!section->psk_keys) + return "Failed to read PSK secrets"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = secrets for PSK authentication", + "PSKsecrets"); + break; + } + +#endif /* !defined(OPENSSL_NO_PSK) */ + + /* pty */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.pty=0; + break; + case CMD_SET_COPY: + section->option.pty=new_service_options.option.pty; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "pty")) + break; + if(!strcasecmp(arg, "yes")) + section->option.pty=1; + else if(!strcasecmp(arg, "no")) + section->option.pty=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no allocate pseudo terminal for 'exec' option", + "pty"); + break; + } +#endif + + /* redirect */ + switch(cmd) { + case CMD_SET_DEFAULTS: + addrlist_clear(§ion->redirect_addr, 0); + break; + case CMD_SET_COPY: + addrlist_clear(§ion->redirect_addr, 0); + name_list_dup(§ion->redirect_addr.names, + new_service_options.redirect_addr.names); + break; + case CMD_FREE: + name_list_free(section->redirect_addr.names); + str_free(section->redirect_addr.addr); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "redirect")) + break; + name_list_append(§ion->redirect_addr.names, arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->redirect_addr.names) { + if(section->option.client) + return "\"redirect\" is unsupported in client sections"; + if(section->option.connect_before_ssl) + return "\"redirect\" is incompatible with the specified protocol negotiation"; + if(!section->option.delayed_lookup && + !addrlist_resolve(§ion->redirect_addr)) { + s_log(LOG_INFO, + "Cannot resolve redirect target - delaying DNS lookup"); + section->connect_addr.num=0; + section->redirect_addr.num=0; + section->option.delayed_lookup=1; + } + if(!section->option.verify_chain && !section->option.verify_peer) + return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled for \"redirect\" to work"; + } + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = [host:]port to redirect on authentication failures", + "redirect"); + break; + } + + /* renegotiation */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.renegotiation=1; + break; + case CMD_SET_COPY: + section->option.renegotiation=new_service_options.option.renegotiation; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "renegotiation")) + break; + if(!strcasecmp(arg, "yes")) + section->option.renegotiation=1; + else if(!strcasecmp(arg, "no")) + section->option.renegotiation=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no support renegotiation", + "renegotiation"); + break; + } + + /* requireCert */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.require_cert=0; + break; + case CMD_SET_COPY: + section->option.require_cert=new_service_options.option.require_cert; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "requireCert")) + break; + if(!strcasecmp(arg, "yes")) { + section->option.request_cert=1; + section->option.require_cert=1; + } else if(!strcasecmp(arg, "no")) { + section->option.require_cert=0; + } else { + return "The argument needs to be either 'yes' or 'no'"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no require client certificate", + "requireCert"); + break; + } + + /* reset */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.reset=1; /* enabled by default */ + break; + case CMD_SET_COPY: + section->option.reset=new_service_options.option.reset; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "reset")) + break; + if(!strcasecmp(arg, "yes")) + section->option.reset=1; + else if(!strcasecmp(arg, "no")) + section->option.reset=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no send TCP RST on error", + "reset"); + break; + } + + /* retry */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.retry=0; + break; + case CMD_SET_COPY: + section->option.retry=new_service_options.option.retry; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "retry")) + break; + if(!strcasecmp(arg, "yes")) + section->option.retry=1; + else if(!strcasecmp(arg, "no")) + section->option.retry=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no retry connect+exec section", + "retry"); + break; + } + +#if OPENSSL_VERSION_NUMBER>=0x10100000L + /* securityLevel */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->security_level=-1; + break; + case CMD_SET_COPY: + section->security_level=new_service_options.security_level; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "securityLevel")) + break; + { + char *tmp_str; + int tmp_int =(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str || tmp_int<0 || tmp_int>5) /* not a correct number */ + return "Illegal security level"; + section->security_level = tmp_int; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d", "securityLevel", DEFAULT_SECURITY_LEVEL); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = set the security level", "securityLevel"); + break; + } +#endif /* OpenSSL 1.1.0 or later */ + +#ifndef USE_WIN32 + /* service */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->servname=str_dup_detached("stunnel"); + break; + case CMD_SET_COPY: + /* servname is *not* copied from the global section */ + break; + case CMD_FREE: + /* deallocation is performed at the end CMD_FREE */ + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "service")) + break; + str_free(section->servname); + section->servname=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = service name", "service"); + break; + } +#endif + +#ifndef USE_WIN32 + /* setgid */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->gid=0; + break; + case CMD_SET_COPY: + section->gid=new_service_options.gid; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "setgid")) + break; + gr=getgrnam(arg); + if(gr) { + section->gid=gr->gr_gid; + return NULL; /* OK */ + } + { + char *tmp_str; + section->gid=(gid_t)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal GID"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = groupname for setgid()", "setgid"); + break; + } +#endif + +#ifndef USE_WIN32 + /* setuid */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->uid=0; + break; + case CMD_SET_COPY: + section->uid=new_service_options.uid; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "setuid")) + break; + pw=getpwnam(arg); + if(pw) { + section->uid=pw->pw_uid; + return NULL; /* OK */ + } + { + char *tmp_str; + section->uid=(uid_t)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal UID"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = username for setuid()", "setuid"); + break; + } +#endif + + /* sessionCacheSize */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->session_size=1000L; + break; + case CMD_SET_COPY: + section->session_size=new_service_options.session_size; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sessionCacheSize")) + break; + { + char *tmp_str; + section->session_size=strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal session cache size"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %ld", "sessionCacheSize", 1000L); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = session cache size", "sessionCacheSize"); + break; + } + + /* sessionCacheTimeout */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->session_timeout=300L; + break; + case CMD_SET_COPY: + section->session_timeout=new_service_options.session_timeout; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sessionCacheTimeout") && strcasecmp(opt, "session")) + break; + { + char *tmp_str; + section->session_timeout=strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal session cache timeout"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %ld seconds", "sessionCacheTimeout", 300L); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = session cache timeout (in seconds)", + "sessionCacheTimeout"); + break; + } + + /* sessionResume */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.session_resume=1; /* enabled by default */ + break; + case CMD_SET_COPY: + section->option.session_resume=new_service_options.option.session_resume; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sessionResume")) + break; + if(!strcasecmp(arg, "yes")) + section->option.session_resume=1; + else if(!strcasecmp(arg, "no")) + section->option.session_resume=0; + else + return "The argument needs to be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no enable session resumption", + "sessionResume"); + break; + } + + /* sessiond */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.sessiond=0; + memset(§ion->sessiond_addr, 0, sizeof(SOCKADDR_UNION)); + section->sessiond_addr.in.sin_family=AF_INET; + break; + case CMD_SET_COPY: + section->option.sessiond=new_service_options.option.sessiond; + memcpy(§ion->sessiond_addr, &new_service_options.sessiond_addr, + sizeof(SOCKADDR_UNION)); + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sessiond")) + break; + section->option.sessiond=1; +#ifdef SSL_OP_NO_TICKET + /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ + /* this prevents session callbacks from being executed */ + section->ssl_options_set|=SSL_OP_NO_TICKET; +#endif + if(!name2addr(§ion->sessiond_addr, arg, 0)) + return "Failed to resolve sessiond server address"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = [host:]port use sessiond at host:port", + "sessiond"); + break; + } + +#ifndef OPENSSL_NO_TLSEXT + /* sni */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->servername_list_head=NULL; + section->servername_list_tail=NULL; + break; + case CMD_SET_COPY: + section->sni= + str_dup_detached(new_service_options.sni); + break; + case CMD_FREE: + str_free(section->sni); + sni_free(section); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sni")) + break; + str_free(section->sni); + section->sni=str_dup_detached(arg); + return NULL; /* OK */ + case CMD_INITIALIZE: + { + const char *tmp_str=sni_init(section); + if(tmp_str) + return tmp_str; + } + if(!section->option.client && section->sni) + ++endpoints; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = master_service:host_name for an SNI virtual service", + "sni"); + break; + } +#endif /* !defined(OPENSSL_NO_TLSEXT) */ + + /* socket */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->sock_opts=socket_options_init(); + break; + case CMD_SET_COPY: + section->sock_opts=socket_options_dup(new_service_options.sock_opts); + break; + case CMD_FREE: + socket_options_free(section->sock_opts); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "socket")) + break; + if(socket_option_parse(section->sock_opts, arg)) + return "Illegal socket option"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = a|l|r:option=value[:value]", "socket"); + s_log(LOG_NOTICE, "%25sset an option on accept/local/remote socket", ""); + break; + } + +#if OPENSSL_VERSION_NUMBER>=0x10100000L + + /* sslVersion */ + switch(cmd) { + case CMD_SET_DEFAULTS: + /* handled in sslVersionMax and sslVersionMin */ + break; + case CMD_SET_COPY: + /* handled in sslVersionMax and sslVersionMin */ + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sslVersion")) + break; + section->max_proto_version= + section->min_proto_version=str_to_proto_version(arg); + if(section->max_proto_version==-1) + return "Invalid protocol version"; + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->max_proto_version && section->min_proto_version && + section->max_proto_versionmin_proto_version) + return "Invalid protocol version range"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = all" + "|SSLv3|TLSv1|TLSv1.1|TLSv1.2" +#ifdef TLS1_3_VERSION + "|TLSv1.3" +#endif + " TLS version", "sslVersion"); + break; + } + + /* sslVersionMax */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->max_proto_version=0; /* highest supported */ + break; + case CMD_SET_COPY: + section->max_proto_version=new_service_options.max_proto_version; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sslVersionMax")) + break; + section->max_proto_version=str_to_proto_version(arg); + if(section->max_proto_version==-1) + return "Invalid protocol version"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = all" + "|SSLv3|TLSv1|TLSv1.1|TLSv1.2" +#ifdef TLS1_3_VERSION + "|TLSv1.3" +#endif + " TLS version", "sslVersionMax"); + break; + } + + /* sslVersionMin */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->min_proto_version=TLS1_VERSION; + break; + case CMD_SET_COPY: + section->min_proto_version=new_service_options.min_proto_version; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sslVersionMin")) + break; + section->min_proto_version=str_to_proto_version(arg); + if(section->min_proto_version==-1) + return "Invalid protocol version"; + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = all" + "|SSLv3|TLSv1|TLSv1.1|TLSv1.2" +#ifdef TLS1_3_VERSION + "|TLSv1.3" +#endif + " TLS version", "sslVersionMin"); + break; + } + +#else /* OPENSSL_VERSION_NUMBER<0x10100000L */ + + /* sslVersion */ + switch(cmd) { + case CMD_SET_DEFAULTS: + tls_methods_set(section, NULL); + break; + case CMD_SET_COPY: + section->client_method=new_service_options.client_method; + section->server_method=new_service_options.server_method; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "sslVersion")) + break; + return tls_methods_set(section, arg); + case CMD_INITIALIZE: + { + char *tmp_str=tls_methods_check(section); + if(tmp_str) + return tmp_str; + } + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = all" + "|SSLv2|SSLv3|TLSv1" +#if OPENSSL_VERSION_NUMBER>=0x10001000L + "|TLSv1.1|TLSv1.2" +#endif /* OPENSSL_VERSION_NUMBER>=0x10001000L */ + " TLS method", "sslVersion"); + break; + } + +#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */ + +#ifndef USE_FORK + /* stack */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->stack_size=DEFAULT_STACK_SIZE; + break; + case CMD_SET_COPY: + section->stack_size=new_service_options.stack_size; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "stack")) + break; + { + char *tmp_str; + section->stack_size=(size_t)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal thread stack size"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d bytes", "stack", DEFAULT_STACK_SIZE); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = thread stack size (in bytes)", "stack"); + break; + } +#endif + +#if OPENSSL_VERSION_NUMBER>=0x10000000L + + /* ticketKeySecret */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ticket_key=NULL; + break; + case CMD_SET_COPY: + section->ticket_key=key_dup(new_service_options.ticket_key); + break; + case CMD_FREE: + key_free(section->ticket_key); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ticketKeySecret")) + break; + section->ticket_key=key_read(arg, "ticketKeySecret"); + if(!section->ticket_key) + return "Failed to read ticketKeySecret"; + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!section->ticket_key) /* ticketKeySecret not configured */ + break; + if(section->option.client){ + s_log(LOG_NOTICE, + "ticketKeySecret is ignored in the client mode"); + break; + } + if(section->ticket_key && !section->ticket_mac) + return "\"ticketKeySecret\" and \"ticketMacSecret\" must be set together"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = secret key for encryption/decryption TLSv1.3 tickets", + "ticketKeySecret"); + break; + } + + /* ticketMacSecret */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->ticket_mac=NULL; + break; + case CMD_SET_COPY: + section->ticket_mac=key_dup(new_service_options.ticket_mac); + break; + case CMD_FREE: + key_free(section->ticket_mac); + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "ticketMacSecret")) + break; + section->ticket_mac=key_read(arg, "ticketMacSecret"); + if(!section->ticket_mac) + return "Failed to read ticketMacSecret"; + return NULL; /* OK */ + case CMD_INITIALIZE: + if(!section->ticket_mac) /* ticketMacSecret not configured */ + break; + if(section->option.client){ + s_log(LOG_NOTICE, + "ticketMacSecret is ignored in the client mode"); + break; + } + if(section->ticket_mac && !section->ticket_key) + return "\"ticketKeySecret\" and \"ticketMacSecret\" must be set together"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = key for HMAC operations on TLSv1.3 tickets", + "ticketMacSecret"); + break; + } + +#endif /* OpenSSL 1.0.0 or later */ + + /* TIMEOUTbusy */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->timeout_busy=300; /* 5 minutes */ + break; + case CMD_SET_COPY: + section->timeout_busy=new_service_options.timeout_busy; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "TIMEOUTbusy")) + break; + { + char *tmp_str; + section->timeout_busy=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal busy timeout"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTbusy", 300); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = seconds to wait for expected data", "TIMEOUTbusy"); + break; + } + + /* TIMEOUTclose */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->timeout_close=60; /* 1 minute */ + break; + case CMD_SET_COPY: + section->timeout_close=new_service_options.timeout_close; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "TIMEOUTclose")) + break; + { + char *tmp_str; + section->timeout_close=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal close timeout"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTclose", 60); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = seconds to wait for close_notify", + "TIMEOUTclose"); + break; + } + + /* TIMEOUTconnect */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->timeout_connect=10; /* 10 seconds */ + break; + case CMD_SET_COPY: + section->timeout_connect=new_service_options.timeout_connect; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "TIMEOUTconnect")) + break; + { + char *tmp_str; + section->timeout_connect=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal connect timeout"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTconnect", 10); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = seconds to connect remote host", "TIMEOUTconnect"); + break; + } + + /* TIMEOUTidle */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->timeout_idle=43200; /* 12 hours */ + break; + case CMD_SET_COPY: + section->timeout_idle=new_service_options.timeout_idle; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "TIMEOUTidle")) + break; + { + char *tmp_str; + section->timeout_idle=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return "Illegal idle timeout"; + return NULL; /* OK */ + } + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTidle", 43200); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = seconds to keep an idle connection", "TIMEOUTidle"); + break; + } + + /* transparent */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.transparent_src=0; + section->option.transparent_dst=0; + break; + case CMD_SET_COPY: + section->option.transparent_src=new_service_options.option.transparent_src; + section->option.transparent_dst=new_service_options.option.transparent_dst; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "transparent")) + break; + if(!strcasecmp(arg, "none") || !strcasecmp(arg, "no")) { + section->option.transparent_src=0; + section->option.transparent_dst=0; + } else if(!strcasecmp(arg, "source") || !strcasecmp(arg, "yes")) { + section->option.transparent_src=1; + section->option.transparent_dst=0; + } else if(!strcasecmp(arg, "destination")) { + section->option.transparent_src=0; + section->option.transparent_dst=1; + } else if(!strcasecmp(arg, "both")) { + section->option.transparent_src=1; + section->option.transparent_dst=1; + } else + return "Selected transparent proxy mode is not available"; + return NULL; /* OK */ + case CMD_INITIALIZE: + if(section->option.transparent_dst) + ++endpoints; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = none|source|destination|both transparent proxy mode", + "transparent"); + break; + } +#endif + + /* verify */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.request_cert=0; + break; + case CMD_SET_COPY: + section->option.request_cert=new_service_options.option.request_cert; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "verify")) + break; + { + char *tmp_str; + int tmp_int=(int)strtol(arg, &tmp_str, 10); + if(tmp_str==arg || *tmp_str || tmp_int<0 || tmp_int>4) + return "Bad verify level"; + section->option.request_cert=1; + section->option.require_cert=(tmp_int>=2); + section->option.verify_chain=(tmp_int>=1 && tmp_int<=3); + section->option.verify_peer=(tmp_int>=3); + } + return NULL; /* OK */ + case CMD_INITIALIZE: + if((section->option.verify_chain || section->option.verify_peer) && + !section->ca_file && !section->ca_dir) + return "Either \"CAfile\" or \"CApath\" has to be configured"; + break; + case CMD_PRINT_DEFAULTS: + s_log(LOG_NOTICE, "%-22s = none", "verify"); + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, + "%-22s = level of peer certificate verification", "verify"); + break; + } + + /* verifyChain */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.verify_chain=0; + break; + case CMD_SET_COPY: + section->option.verify_chain=new_service_options.option.verify_chain; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "verifyChain")) + break; + if(!strcasecmp(arg, "yes")) { + section->option.request_cert=1; + section->option.require_cert=1; + section->option.verify_chain=1; + } else if(!strcasecmp(arg, "no")) { + section->option.verify_chain=0; + } else { + return "The argument needs to be either 'yes' or 'no'"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no verify certificate chain", + "verifyChain"); + break; + } + + /* verifyPeer */ + switch(cmd) { + case CMD_SET_DEFAULTS: + section->option.verify_peer=0; + break; + case CMD_SET_COPY: + section->option.verify_peer=new_service_options.option.verify_peer; + break; + case CMD_FREE: + break; + case CMD_SET_VALUE: + if(strcasecmp(opt, "verifyPeer")) + break; + if(!strcasecmp(arg, "yes")) { + section->option.request_cert=1; + section->option.require_cert=1; + section->option.verify_peer=1; + } else if(!strcasecmp(arg, "no")) { + section->option.verify_peer=0; + } else { + return "The argument needs to be either 'yes' or 'no'"; + } + return NULL; /* OK */ + case CMD_INITIALIZE: + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + s_log(LOG_NOTICE, "%-22s = yes|no verify peer certificate", + "verifyPeer"); + break; + } + + /* final checks */ + switch(cmd) { + case CMD_SET_DEFAULTS: + break; + case CMD_SET_COPY: + break; + case CMD_FREE: + str_free(section->chain); + if(section->session) + SSL_SESSION_free(section->session); + if(section->ctx) + SSL_CTX_free(section->ctx); + str_free(section->servname); + if(section==&service_options || section==&new_service_options) + memset(section, 0, sizeof(SERVICE_OPTIONS)); + else + str_free(section); + break; + case CMD_SET_VALUE: + return option_not_found; + case CMD_INITIALIZE: + if(section!=&new_service_options) { /* daemon mode checks */ + if(endpoints!=2) + return "Each service must define two endpoints"; + } else { /* inetd mode checks */ + if(section->option.accept) + return "'accept' option is only allowed in a [section]"; + /* no need to check for section->sni in inetd mode, + as it requires valid sections to be set */ + if(endpoints!=1) + return "Inetd mode must define one endpoint"; + } +#ifdef SSL_OP_NO_TICKET + /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ + /* OpenSSL 1.1.1 is required to serialize application data + * into session tickets */ + /* server mode sections need it for the "redirect" option + * and connect address session persistence */ + if(OpenSSL_version_num()<0x10101000L && + !section->option.client && + !section->option.connect_before_ssl) + section->ssl_options_set|=SSL_OP_NO_TICKET; +#endif /* SSL_OP_NO_TICKET */ + if(context_init(section)) /* initialize TLS context */ + return "Failed to initialize TLS context"; + break; + case CMD_PRINT_DEFAULTS: + break; + case CMD_PRINT_HELP: + break; + } + + return NULL; /* OK */ +} + +/**************************************** validate and initialize configuration */ + +#ifndef OPENSSL_NO_TLSEXT + +NOEXPORT const char *sni_init(SERVICE_OPTIONS *section) { + char *tmp_str; + SERVICE_OPTIONS *tmpsrv; + + /* server mode: update servername_list based on the SNI option */ + if(!section->option.client && section->sni) { + tmp_str=strchr(section->sni, ':'); + if(!tmp_str) + return "Invalid SNI parameter format"; + *tmp_str++='\0'; + for(tmpsrv=new_service_options.next; tmpsrv; tmpsrv=tmpsrv->next) + if(!strcmp(tmpsrv->servname, section->sni)) + break; + if(!tmpsrv) + return "SNI section name not found"; + if(tmpsrv->option.client) + return "SNI master service is a TLS client"; + if(tmpsrv->servername_list_tail) { + tmpsrv->servername_list_tail->next=str_alloc_detached(sizeof(SERVERNAME_LIST)); + tmpsrv->servername_list_tail=tmpsrv->servername_list_tail->next; + } else { /* first virtual service */ + tmpsrv->servername_list_head= + tmpsrv->servername_list_tail= + str_alloc_detached(sizeof(SERVERNAME_LIST)); + tmpsrv->ssl_options_set|= + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + } + /* a slave section reference is needed to prevent a race condition + while switching to a section after configuration file reload */ + service_up_ref(section); + tmpsrv->servername_list_tail->servername=str_dup_detached(tmp_str); + tmpsrv->servername_list_tail->opt=section; + tmpsrv->servername_list_tail->next=NULL; + /* always negotiate a new session on renegotiation, as the TLS + * context settings (including access control) may be different */ + section->ssl_options_set|= + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + } + + /* client mode: setup SNI default based on 'protocolHost' and 'connect' options */ + if(section->option.client && !section->sni) { + /* setup host_name for SNI, prefer SNI and protocolHost if specified */ + if(section->protocol_host) /* 'protocolHost' option */ + section->sni=str_dup_detached(section->protocol_host); + else if(section->connect_addr.names) /* 'connect' option */ + section->sni=str_dup_detached(section->connect_addr.names->name); /* first hostname */ + if(section->sni) { /* either 'protocolHost' or 'connect' specified */ + tmp_str=strrchr(section->sni, ':'); + if(tmp_str) { /* 'host:port' -> drop ':port' */ + *tmp_str='\0'; + } else { /* 'port' -> default to 'localhost' */ + str_free(section->sni); + section->sni=str_dup_detached("localhost"); + } + } + } + return NULL; +} + +NOEXPORT void sni_free(SERVICE_OPTIONS *section) { + SERVERNAME_LIST *curr=section->servername_list_head; + while(curr) { + SERVERNAME_LIST *next=curr->next; + str_free(curr->servername); + service_free(curr->opt); /* free the slave section */ + str_free(curr); + curr=next; + } + section->servername_list_head=NULL; + section->servername_list_tail=NULL; +} + +#endif /* !defined(OPENSSL_NO_TLSEXT) */ + +/**************************************** modern TLS version handling */ + +#if OPENSSL_VERSION_NUMBER>=0x10100000L + +NOEXPORT int str_to_proto_version(const char *name) { + if(!strcasecmp(name, "all")) + return 0; + if(!strcasecmp(name, "SSLv3")) + return SSL3_VERSION; + if(!strcasecmp(name, "TLSv1")) + return TLS1_VERSION; + if(!strcasecmp(name, "TLSv1.1")) + return TLS1_1_VERSION; + if(!strcasecmp(name, "TLSv1.2")) + return TLS1_2_VERSION; +#ifdef TLS1_3_VERSION + if(!strcasecmp(name, "TLSv1.3")) + return TLS1_3_VERSION; +#endif + return -1; +} + +/**************************************** deprecated TLS version handling */ + +#else /* OPENSSL_VERSION_NUMBER<0x10100000L */ + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif /* __GNUC__ */ + +NOEXPORT char *tls_methods_set(SERVICE_OPTIONS *section, const char *arg) { + if(!arg) { /* defaults */ + section->client_method=(SSL_METHOD *)SSLv23_client_method(); + section->server_method=(SSL_METHOD *)SSLv23_server_method(); + } else if(!strcasecmp(arg, "all")) { + section->client_method=(SSL_METHOD *)SSLv23_client_method(); + section->server_method=(SSL_METHOD *)SSLv23_server_method(); + } else if(!strcasecmp(arg, "SSLv2")) { +#ifndef OPENSSL_NO_SSL2 + section->client_method=(SSL_METHOD *)SSLv2_client_method(); + section->server_method=(SSL_METHOD *)SSLv2_server_method(); +#else /* OPENSSL_NO_SSL2 */ + return "SSLv2 not supported"; +#endif /* !OPENSSL_NO_SSL2 */ + } else if(!strcasecmp(arg, "SSLv3")) { +#ifndef OPENSSL_NO_SSL3 + section->client_method=(SSL_METHOD *)SSLv3_client_method(); + section->server_method=(SSL_METHOD *)SSLv3_server_method(); +#else /* OPENSSL_NO_SSL3 */ + return "SSLv3 not supported"; +#endif /* !OPENSSL_NO_SSL3 */ + } else if(!strcasecmp(arg, "TLSv1")) { +#ifndef OPENSSL_NO_TLS1 + section->client_method=(SSL_METHOD *)TLSv1_client_method(); + section->server_method=(SSL_METHOD *)TLSv1_server_method(); +#else /* OPENSSL_NO_TLS1 */ + return "TLSv1 not supported"; +#endif /* !OPENSSL_NO_TLS1 */ + } else if(!strcasecmp(arg, "TLSv1.1")) { +#ifndef OPENSSL_NO_TLS1_1 + section->client_method=(SSL_METHOD *)TLSv1_1_client_method(); + section->server_method=(SSL_METHOD *)TLSv1_1_server_method(); +#else /* OPENSSL_NO_TLS1_1 */ + return "TLSv1.1 not supported"; +#endif /* !OPENSSL_NO_TLS1_1 */ + } else if(!strcasecmp(arg, "TLSv1.2")) { +#ifndef OPENSSL_NO_TLS1_2 + section->client_method=(SSL_METHOD *)TLSv1_2_client_method(); + section->server_method=(SSL_METHOD *)TLSv1_2_server_method(); +#else /* OPENSSL_NO_TLS1_2 */ + return "TLSv1.2 not supported"; +#endif /* !OPENSSL_NO_TLS1_2 */ + } else + return "Incorrect version of TLS protocol"; + return NULL; /* OK */ +} + +NOEXPORT char *tls_methods_check(SERVICE_OPTIONS *section) { +#ifdef USE_FIPS + if(new_global_options.option.fips) { +#ifndef OPENSSL_NO_SSL2 + if(section->option.client ? + section->client_method==(SSL_METHOD *)SSLv2_client_method() : + section->server_method==(SSL_METHOD *)SSLv2_server_method()) + return "\"sslVersion = SSLv2\" not supported in FIPS mode"; +#endif /* !OPENSSL_NO_SSL2 */ +#ifndef OPENSSL_NO_SSL3 + if(section->option.client ? + section->client_method==(SSL_METHOD *)SSLv3_client_method() : + section->server_method==(SSL_METHOD *)SSLv3_server_method()) + return "\"sslVersion = SSLv3\" not supported in FIPS mode"; +#endif /* !OPENSSL_NO_SSL3 */ + } +#else /* USE_FIPS */ + (void)section; /* squash the unused parameter warning */ +#endif /* USE_FIPS */ + return NULL; +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif /* __GNUC__ */ + +#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */ + +/**************************************** facility/debug level */ + +typedef struct { + const char *name; + int value; +} facilitylevel; + +NOEXPORT const char *parse_debug_level(char *arg, SERVICE_OPTIONS *section) { + facilitylevel *fl; + +/* facilities only make sense on unix */ +#if !defined (USE_WIN32) && !defined (__vms) + facilitylevel facilities[] = { + {"auth", LOG_AUTH}, {"cron", LOG_CRON}, {"daemon", LOG_DAEMON}, + {"kern", LOG_KERN}, {"lpr", LOG_LPR}, {"mail", LOG_MAIL}, + {"news", LOG_NEWS}, {"syslog", LOG_SYSLOG}, {"user", LOG_USER}, + {"uucp", LOG_UUCP}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1}, + {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4}, + {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7}, + + /* some facilities are not defined on all Unices */ +#ifdef LOG_AUTHPRIV + {"authpriv", LOG_AUTHPRIV}, +#endif +#ifdef LOG_FTP + {"ftp", LOG_FTP}, +#endif +#ifdef LOG_NTP + {"ntp", LOG_NTP}, +#endif + {NULL, 0} + }; +#endif /* USE_WIN32, __vms */ + + facilitylevel levels[] = { + {"emerg", LOG_EMERG}, {"alert", LOG_ALERT}, + {"crit", LOG_CRIT}, {"err", LOG_ERR}, + {"warning", LOG_WARNING}, {"notice", LOG_NOTICE}, + {"info", LOG_INFO}, {"debug", LOG_DEBUG}, + {NULL, -1} + }; + +/* facilities only make sense on Unix */ +#if !defined (USE_WIN32) && !defined (__vms) + if(section==&new_service_options && strchr(arg, '.')) { + /* a facility was specified in the global options */ + new_global_options.log_facility=-1; + arg=strtok(arg, "."); /* break it up */ + + for(fl=facilities; fl->name; ++fl) { + if(!strcasecmp(fl->name, arg)) { + new_global_options.log_facility=fl->value; + break; + } + } + if(new_global_options.log_facility==-1) + return "Illegal syslog facility"; + arg=strtok(NULL, "."); /* set to the remainder */ + } +#endif /* USE_WIN32, __vms */ + + /* time to check the syslog level */ + if(arg && strlen(arg)==1 && *arg>='0' && *arg<='7') { + section->log_level=*arg-'0'; + return NULL; /* OK */ + } + section->log_level=8; /* illegal level */ + for(fl=levels; fl->name; ++fl) { + if(!strcasecmp(fl->name, arg)) { + section->log_level=fl->value; + break; + } + } + if(section->log_level==8) + return "Illegal debug level"; /* FAILED */ + return NULL; /* OK */ +} + +/**************************************** TLS options */ + +NOEXPORT uint64_t parse_ssl_option(char *arg) { + const SSL_OPTION *option; + + for(option=ssl_opts; option->name; ++option) + if(!strcasecmp(option->name, arg)) + return option->value; + return INVALID_SSL_OPTION; /* FAILED */ +} + +NOEXPORT void print_ssl_options(void) { + const SSL_OPTION *option; + + s_log(LOG_NOTICE, " "); + s_log(LOG_NOTICE, "Supported TLS options:"); + for(option=ssl_opts; option->name; ++option) + s_log(LOG_NOTICE, "options = %s", option->name); +} + +/**************************************** read PSK file */ + +#ifndef OPENSSL_NO_PSK + +NOEXPORT PSK_KEYS *psk_read(char *key_file) { + DISK_FILE *df; + char line[CONFLINELEN], *key_str; + unsigned char *key_buf; + long key_len; + PSK_KEYS *head=NULL, *tail=NULL, *curr; + int line_number=0; + + if(file_permissions(key_file)) + return NULL; + df=file_open(key_file, FILE_MODE_READ); + if(!df) { + s_log(LOG_ERR, "Cannot open PSKsecrets file"); + return NULL; + } + while(file_getline(df, line, CONFLINELEN)>=0) { + ++line_number; + if(!line[0]) /* empty line */ + continue; + key_str=strchr(line, ':'); + if(!key_str) { + s_log(LOG_ERR, + "PSKsecrets line %d: Not in identity:key format", + line_number); + file_close(df); + psk_free(head); + return NULL; + } + *key_str++='\0'; + if(strlen(line)+1>PSK_MAX_IDENTITY_LEN) { /* with the trailing '\0' */ + s_log(LOG_ERR, + "PSKsecrets line %d: Identity longer than %d characters", + line_number, PSK_MAX_IDENTITY_LEN); + file_close(df); + psk_free(head); + return NULL; + } + key_buf=OPENSSL_hexstr2buf(key_str, &key_len); + if(key_buf) { /* a valid hexadecimal value */ + s_log(LOG_INFO, "PSKsecrets line %d: " + "%ld-byte hexadecimal key configured for identity \"%s\"", + line_number, key_len, line); + } else { /* not a valid hexadecimal value -> copy as a string */ + key_len=(long)strlen(key_str); + key_buf=OPENSSL_malloc((size_t)key_len); + memcpy(key_buf, key_str, (size_t)key_len); + s_log(LOG_INFO, "PSKsecrets line %d: " + "%ld-byte ASCII key configured for identity \"%s\"", + line_number, key_len, line); + } + if(key_len>PSK_MAX_PSK_LEN) { + s_log(LOG_ERR, + "PSKsecrets line %d: Key longer than %d bytes", + line_number, PSK_MAX_PSK_LEN); + OPENSSL_free(key_buf); + file_close(df); + psk_free(head); + return NULL; + } + if(key_len<16) { + /* shorter keys are unlikely to have sufficient entropy */ + s_log(LOG_ERR, + "PSKsecrets line %d: Key shorter than 16 bytes", + line_number); + OPENSSL_free(key_buf); + file_close(df); + psk_free(head); + return NULL; + } + curr=str_alloc_detached(sizeof(PSK_KEYS)); + curr->identity=str_dup_detached(line); + curr->key_val=str_alloc_detached((size_t)key_len); + memcpy(curr->key_val, key_buf, (size_t)key_len); + OPENSSL_free(key_buf); + curr->key_len=(unsigned)key_len; + curr->next=NULL; + if(head) + tail->next=curr; + else + head=curr; + tail=curr; + } + file_close(df); + return head; +} + +NOEXPORT PSK_KEYS *psk_dup(PSK_KEYS *src) { + PSK_KEYS *head=NULL, *tail=NULL, *curr; + + for(; src; src=src->next) { + curr=str_alloc_detached(sizeof(PSK_KEYS)); + curr->identity=str_dup_detached(src->identity); + curr->key_val=str_alloc_detached(src->key_len); + memcpy(curr->key_val, src->key_val, src->key_len); + curr->key_len=src->key_len; + curr->next=NULL; + if(head) + tail->next=curr; + else + head=curr; + tail=curr; + } + return head; +} + +NOEXPORT void psk_free(PSK_KEYS *head) { + while(head) { + PSK_KEYS *next=head->next; + str_free_const(head->identity); + str_free(head->key_val); + str_free(head); + head=next; + } +} + +#endif + +/**************************************** read ticket key */ + +#if OPENSSL_VERSION_NUMBER>=0x10000000L + +NOEXPORT TICKET_KEY *key_read(char *arg, const char *option) { + char *key_str; + unsigned char *key_buf; + long key_len; + TICKET_KEY *head=NULL; + + key_str=str_dup_detached(arg); + key_buf=OPENSSL_hexstr2buf(key_str, &key_len); + if(key_buf) + if((key_len == 16) || (key_len == 32)) /* a valid 16 or 32 byte hexadecimal value */ + s_log(LOG_INFO, "%s configured", option); + else { /* not a valid length */ + s_log(LOG_ERR, "%s value has %ld bytes instead of required 16 or 32 bytes", + option, key_len); + OPENSSL_free(key_buf); + key_free(head); + return NULL; + } + else { /* not a valid hexadecimal form */ + s_log(LOG_ERR, "Required %s is 16 or 32 byte hexadecimal key", option); + key_free(head); + return NULL; + } + head=str_alloc_detached(sizeof(TICKET_KEY)); + head->key_val=str_alloc_detached((size_t)key_len); + memcpy(head->key_val, key_buf, (size_t)key_len); + OPENSSL_free(key_buf); + head->key_len=(int)key_len; + return head; +} + +NOEXPORT TICKET_KEY *key_dup(TICKET_KEY *src) { + TICKET_KEY *head=NULL; + + if (src) { + head=str_alloc_detached(sizeof(TICKET_KEY)); + head->key_val=(unsigned char *)str_dup_detached((char *)src->key_val); + head->key_len=src->key_len; + } + return head; +} + +NOEXPORT void key_free(TICKET_KEY *head) { + if (head) { + str_free(head->key_val); + str_free(head); + } +} + +#endif /* OpenSSL 1.0.0 or later */ + +/**************************************** socket options */ + +#define VAL_TAB {NULL, NULL, NULL} + +SOCK_OPT sock_opts_def[]={ + {"SO_DEBUG", SOL_SOCKET, SO_DEBUG, TYPE_FLAG, VAL_TAB}, + {"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, TYPE_FLAG, VAL_TAB}, + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, TYPE_FLAG, VAL_TAB}, + {"SO_LINGER", SOL_SOCKET, SO_LINGER, TYPE_LINGER, VAL_TAB}, + {"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, TYPE_FLAG, VAL_TAB}, + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, TYPE_INT, VAL_TAB}, + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, TYPE_INT, VAL_TAB}, +#ifdef SO_RCVLOWAT + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, TYPE_INT, VAL_TAB}, +#endif +#ifdef SO_SNDLOWAT + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, TYPE_INT, VAL_TAB}, +#endif +#ifdef SO_RCVTIMEO + {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, TYPE_TIMEVAL, VAL_TAB}, +#endif +#ifdef SO_SNDTIMEO + {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, TYPE_TIMEVAL, VAL_TAB}, +#endif +#ifdef USE_WIN32 + {"SO_EXCLUSIVEADDRUSE", SOL_SOCKET, SO_EXCLUSIVEADDRUSE, TYPE_FLAG, VAL_TAB}, +#endif + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, VAL_TAB}, +#ifdef SO_BINDTODEVICE + {"SO_BINDTODEVICE", SOL_SOCKET, SO_BINDTODEVICE, TYPE_STRING, VAL_TAB}, +#endif +#ifdef SOL_TCP +#ifdef TCP_KEEPCNT + {"TCP_KEEPCNT", SOL_TCP, TCP_KEEPCNT, TYPE_INT, VAL_TAB}, +#endif +#ifdef TCP_KEEPIDLE + {"TCP_KEEPIDLE", SOL_TCP, TCP_KEEPIDLE, TYPE_INT, VAL_TAB}, +#endif +#ifdef TCP_KEEPINTVL + {"TCP_KEEPINTVL", SOL_TCP, TCP_KEEPINTVL, TYPE_INT, VAL_TAB}, +#endif +#endif /* SOL_TCP */ +#ifdef IP_TOS + {"IP_TOS", IPPROTO_IP, IP_TOS, TYPE_INT, VAL_TAB}, +#endif +#ifdef IP_TTL + {"IP_TTL", IPPROTO_IP, IP_TTL, TYPE_INT, VAL_TAB}, +#endif +#ifdef IP_MAXSEG + {"TCP_MAXSEG", IPPROTO_TCP, TCP_MAXSEG, TYPE_INT, VAL_TAB}, +#endif + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, TYPE_FLAG, VAL_TAB}, +#ifdef IP_FREEBIND + {"IP_FREEBIND", IPPROTO_IP, IP_FREEBIND, TYPE_FLAG, VAL_TAB}, +#endif +#ifdef IP_BINDANY + {"IP_BINDANY", IPPROTO_IP, IP_BINDANY, TYPE_FLAG, VAL_TAB}, +#endif +#ifdef IPV6_BINDANY + {"IPV6_BINDANY", IPPROTO_IPV6, IPV6_BINDANY, TYPE_FLAG, VAL_TAB}, +#endif +#ifdef IPV6_V6ONLY + {"IPV6_V6ONLY", IPPROTO_IPV6, IPV6_V6ONLY, TYPE_FLAG, VAL_TAB}, +#endif + {NULL, 0, 0, TYPE_NONE, VAL_TAB} +}; + +NOEXPORT SOCK_OPT *socket_options_init(void) { +#ifdef USE_WIN32 + DWORD version; + int major, minor; +#endif + + SOCK_OPT *opt=str_alloc_detached(sizeof sock_opts_def); + memcpy(opt, sock_opts_def, sizeof sock_opts_def); + +#ifdef USE_WIN32 +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + version=GetVersion(); + major=LOBYTE(LOWORD(version)); + minor=HIBYTE(LOWORD(version)); + s_log(LOG_DEBUG, "Running on Windows %d.%d", major, minor); + + if(major>5) /* Vista or later */ + socket_option_set_int(opt, "SO_EXCLUSIVEADDRUSE", 0, 1); /* accepting socket */ +#else + socket_option_set_int(opt, "SO_REUSEADDR", 0, 1); /* accepting socket */ +#endif + socket_option_set_int(opt, "TCP_NODELAY", 1, 1); /* local socket */ + socket_option_set_int(opt, "TCP_NODELAY", 2, 1); /* remote socket */ + return opt; +} + +NOEXPORT void socket_option_set_int(SOCK_OPT *opt, const char *name, int type, int value) { + for(; opt->opt_str; ++opt) { + if(!strcmp(name, opt->opt_str)) { + opt->opt_val[type]=str_alloc_detached(sizeof(OPT_UNION)); + opt->opt_val[type]->i_val=value; + } + } +} + +NOEXPORT SOCK_OPT *socket_options_dup(SOCK_OPT *src) { + SOCK_OPT *dst=str_alloc_detached(sizeof sock_opts_def); + SOCK_OPT *ptr; + + memcpy(dst, sock_opts_def, sizeof sock_opts_def); + for(ptr=dst; src->opt_str; ++src, ++ptr) { + int type; + for(type=0; type<3; ++type) { + if(src->opt_val[type]) { + ptr->opt_val[type]=str_alloc_detached(sizeof(OPT_UNION)); + memcpy(ptr->opt_val[type], + src->opt_val[type], sizeof(OPT_UNION)); + } + } + } + return dst; +} + +NOEXPORT void socket_options_free(SOCK_OPT *opt) { + SOCK_OPT *ptr; + if(!opt) { + s_log(LOG_ERR, "INTERNAL ERROR: Socket options not initialized"); + return; + } + for(ptr=opt; ptr->opt_str; ++ptr) { + int type; + for(type=0; type<3; ++type) + str_free(ptr->opt_val[type]); + } + str_free(opt); +} + +NOEXPORT int socket_options_print(void) { + SOCK_OPT *opt, *ptr; + + s_log(LOG_NOTICE, " "); + s_log(LOG_NOTICE, "Socket option defaults:"); + s_log(LOG_NOTICE, + " Option Name | Accept | Local | Remote |OS default"); + s_log(LOG_NOTICE, + " --------------------+----------+----------+----------+----------"); + + opt=socket_options_init(); + for(ptr=opt; ptr->opt_str; ++ptr) { + SOCKET fd; + socklen_t optlen; + OPT_UNION val; + char *ta, *tl, *tr, *td; + + /* get OS default value */ +#if defined(AF_INET6) && defined(IPPROTO_IPV6) + if(ptr->opt_level==IPPROTO_IPV6) + fd=socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + fd=socket(AF_INET, SOCK_STREAM, 0); + optlen=sizeof val; + if(getsockopt(fd, ptr->opt_level, + ptr->opt_name, (void *)&val, &optlen)) { + switch(get_last_socket_error()) { + case S_ENOPROTOOPT: + case S_EOPNOTSUPP: + td=str_dup("write-only"); + break; + default: + s_log(LOG_ERR, "Failed to get %s OS default", ptr->opt_str); + sockerror("getsockopt"); + closesocket(fd); + return 1; /* FAILED */ + } + } else + td=socket_option_text(ptr->opt_type, &val); + closesocket(fd); + + /* get stunnel default values */ + ta=socket_option_text(ptr->opt_type, ptr->opt_val[0]); + tl=socket_option_text(ptr->opt_type, ptr->opt_val[1]); + tr=socket_option_text(ptr->opt_type, ptr->opt_val[2]); + + /* print collected data and free the allocated memory */ + s_log(LOG_NOTICE, " %-20s|%10s|%10s|%10s|%10s", + ptr->opt_str, ta, tl, tr, td); + str_free(ta); str_free(tl); str_free(tr); str_free(td); + } + socket_options_free(opt); + return 0; /* OK */ +} + +NOEXPORT char *socket_option_text(VAL_TYPE type, OPT_UNION *val) { + if(!val) + return str_dup(" -- "); + switch(type) { + case TYPE_FLAG: + return str_printf("%s", val->i_val ? "yes" : "no"); + case TYPE_INT: + return str_printf("%d", val->i_val); + case TYPE_LINGER: + return str_printf("%d:%d", + val->linger_val.l_onoff, val->linger_val.l_linger); + case TYPE_TIMEVAL: + return str_printf("%d:%d", + (int)val->timeval_val.tv_sec, (int)val->timeval_val.tv_usec); + case TYPE_STRING: + return str_printf("%s", val->c_val); + case TYPE_NONE: + return str_dup(" none "); /* internal error? */ + } + return str_dup(" Ooops? "); /* internal error? */ +} + +NOEXPORT int socket_option_parse(SOCK_OPT *opt, char *arg) { + int socket_type; /* 0-accept, 1-local, 2-remote */ + char *opt_val_str, *opt_val2_str, *tmp_str; + OPT_UNION opt_val; + + if(arg[1]!=':') + return 1; /* FAILED */ + switch(arg[0]) { + case 'a': + socket_type=0; break; + case 'l': + socket_type=1; break; + case 'r': + socket_type=2; break; + default: + return 1; /* FAILED */ + } + arg+=2; + opt_val_str=strchr(arg, '='); + if(!opt_val_str) /* no '='? */ + return 1; /* FAILED */ + *opt_val_str++='\0'; + + for(; opt->opt_str && strcmp(arg, opt->opt_str); ++opt) + ; + if(!opt->opt_str) + return 1; /* FAILED */ + + switch(opt->opt_type) { + case TYPE_FLAG: + if(!strcasecmp(opt_val_str, "yes") || !strcmp(opt_val_str, "1")) { + opt_val.i_val=1; + break; /* OK */ + } + if(!strcasecmp(opt_val_str, "no") || !strcmp(opt_val_str, "0")) { + opt_val.i_val=0; + break; /* OK */ + } + return 1; /* FAILED */ + case TYPE_INT: + opt_val.i_val=(int)strtol(opt_val_str, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return 1; /* FAILED */ + break; /* OK */ + case TYPE_LINGER: + opt_val2_str=strchr(opt_val_str, ':'); + if(opt_val2_str) { + *opt_val2_str++='\0'; + opt_val.linger_val.l_linger= + (u_short)strtol(opt_val2_str, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return 1; /* FAILED */ + } else { + opt_val.linger_val.l_linger=0; + } + opt_val.linger_val.l_onoff= + (u_short)strtol(opt_val_str, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return 1; /* FAILED */ + break; /* OK */ + case TYPE_TIMEVAL: + opt_val2_str=strchr(opt_val_str, ':'); + if(opt_val2_str) { + *opt_val2_str++='\0'; + opt_val.timeval_val.tv_usec= + (int)strtol(opt_val2_str, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return 1; /* FAILED */ + } else { + opt_val.timeval_val.tv_usec=0; + } + opt_val.timeval_val.tv_sec= + (int)strtol(opt_val_str, &tmp_str, 10); + if(tmp_str==arg || *tmp_str) /* not a number */ + return 1; /* FAILED */ + break; /* OK */ + case TYPE_STRING: + if(strlen(opt_val_str)+1>sizeof(OPT_UNION)) + return 1; /* FAILED */ + strcpy(opt_val.c_val, opt_val_str); + break; /* OK */ + default: + return 1; /* FAILED */ + } + str_free(opt->opt_val[socket_type]); + opt->opt_val[socket_type]=str_alloc_detached(sizeof(OPT_UNION)); + memcpy(opt->opt_val[socket_type], &opt_val, sizeof(OPT_UNION)); + return 0; +} + +/**************************************** OCSP */ + +#ifndef OPENSSL_NO_OCSP + +NOEXPORT unsigned long parse_ocsp_flag(char *arg) { + struct { + const char *name; + unsigned long value; + } ocsp_opts[] = { + {"NOCERTS", OCSP_NOCERTS}, + {"NOINTERN", OCSP_NOINTERN}, + {"NOSIGS", OCSP_NOSIGS}, + {"NOCHAIN", OCSP_NOCHAIN}, + {"NOVERIFY", OCSP_NOVERIFY}, + {"NOEXPLICIT", OCSP_NOEXPLICIT}, + {"NOCASIGN", OCSP_NOCASIGN}, + {"NODELEGATED", OCSP_NODELEGATED}, + {"NOCHECKS", OCSP_NOCHECKS}, + {"TRUSTOTHER", OCSP_TRUSTOTHER}, + {"RESPID_KEY", OCSP_RESPID_KEY}, + {"NOTIME", OCSP_NOTIME}, + {NULL, 0} + }, *option; + + for(option=ocsp_opts; option->name; ++option) + if(!strcasecmp(option->name, arg)) + return option->value; + return 0; /* FAILED */ +} + +#endif /* !defined(OPENSSL_NO_OCSP) */ + +/**************************************** engine */ + +#ifndef OPENSSL_NO_ENGINE + +#define MAX_ENGINES 256 +static ENGINE *engines[MAX_ENGINES]; /* table of engines for config parser */ +static int current_engine; +static int engine_initialized; + +NOEXPORT void engine_reset_list(void) { + current_engine=-1; + engine_initialized=1; +} + +NOEXPORT const char *engine_auto(void) { + ENGINE *e; + + s_log(LOG_INFO, "Enabling automatic engine support"); + ENGINE_register_all_complete(); + /* rebuild the internal list of engines */ + engine_reset_list(); + for(e=ENGINE_get_first(); e; e=ENGINE_get_next(e)) { + if(++current_engine>=MAX_ENGINES) + return "Too many open engines"; + engines[current_engine]=e; + s_log(LOG_INFO, "Engine #%d (%s) registered", + current_engine+1, ENGINE_get_id(e)); + } + s_log(LOG_INFO, "Automatic engine support enabled"); + return NULL; /* OK */ +} + +NOEXPORT const char *engine_open(const char *name) { + engine_init(); /* initialize the previous engine (if any) */ + if(++current_engine>=MAX_ENGINES) + return "Too many open engines"; + s_log(LOG_DEBUG, "Enabling support for engine \"%s\"", name); + engines[current_engine]=ENGINE_by_id(name); + if(!engines[current_engine]) { + sslerror("ENGINE_by_id"); + return "Failed to open the engine"; + } + engine_initialized=0; + if(ENGINE_ctrl(engines[current_engine], ENGINE_CTRL_SET_USER_INTERFACE, + 0, ui_stunnel(), NULL)) { + s_log(LOG_NOTICE, "UI set for engine #%d (%s)", + current_engine+1, ENGINE_get_id(engines[current_engine])); + } else { + ERR_clear_error(); + s_log(LOG_INFO, "UI not supported by engine #%d (%s)", + current_engine+1, ENGINE_get_id(engines[current_engine])); + } + return NULL; /* OK */ +} + +NOEXPORT const char *engine_ctrl(const char *cmd, const char *arg) { + if(current_engine<0) + return "No engine was defined"; + if(!strcasecmp(cmd, "INIT")) /* special control command */ + return engine_init(); + if(arg) + s_log(LOG_DEBUG, "Executing engine control command %s:%s", cmd, arg); + else + s_log(LOG_DEBUG, "Executing engine control command %s", cmd); + if(!ENGINE_ctrl_cmd_string(engines[current_engine], cmd, arg, 0)) { + sslerror("ENGINE_ctrl_cmd_string"); + return "Failed to execute the engine control command"; + } + return NULL; /* OK */ +} + +NOEXPORT const char *engine_default(const char *list) { + if(current_engine<0) + return "No engine was defined"; + if(!ENGINE_set_default_string(engines[current_engine], list)) { + sslerror("ENGINE_set_default_string"); + return "Failed to set engine as default"; + } + s_log(LOG_INFO, "Engine #%d (%s) set as default for %s", + current_engine+1, ENGINE_get_id(engines[current_engine]), list); + return NULL; +} + +NOEXPORT const char *engine_init(void) { + if(engine_initialized) /* either first or already initialized */ + return NULL; /* OK */ + s_log(LOG_DEBUG, "Initializing engine #%d (%s)", + current_engine+1, ENGINE_get_id(engines[current_engine])); + if(!ENGINE_init(engines[current_engine])) { + if(ERR_peek_last_error()) /* really an error */ + sslerror("ENGINE_init"); + else + s_log(LOG_ERR, "Engine #%d (%s) not initialized", + current_engine+1, ENGINE_get_id(engines[current_engine])); + return "Engine initialization failed"; + } +#if 0 + /* it is a bad idea to set the engine as default for all sections */ + /* the "engine=auto" or "engineDefault" options should be used instead */ + if(!ENGINE_set_default(engines[current_engine], ENGINE_METHOD_ALL)) { + sslerror("ENGINE_set_default"); + return "Selecting default engine failed"; + } +#endif + + s_log(LOG_INFO, "Engine #%d (%s) initialized", + current_engine+1, ENGINE_get_id(engines[current_engine])); + engine_initialized=1; + return NULL; /* OK */ +} + +NOEXPORT ENGINE *engine_get_by_id(const char *id) { + int i; + + for(i=0; i<=current_engine; ++i) + if(!strcmp(id, ENGINE_get_id(engines[i]))) + return engines[i]; + return NULL; +} + +NOEXPORT ENGINE *engine_get_by_num(const int i) { + if(i<0 || i>current_engine) + return NULL; + return engines[i]; +} + +#endif /* !defined(OPENSSL_NO_ENGINE) */ + +/**************************************** include config directory */ + +NOEXPORT const char *include_config(char *directory, SERVICE_OPTIONS **section_ptr) { + struct dirent **namelist; + int i, num, err=0; + + num=scandir(directory, &namelist, NULL, alphasort); + if(num<0) { + ioerror("scandir"); + return "Failed to include directory"; + } + for(i=0; id_name); + if(!stat(name, &sb) && S_ISREG(sb.st_mode)) + err=options_file(name, CONF_FILE, section_ptr); + else + s_log(LOG_DEBUG, "\"%s\" is not a file", name); + str_free(name); + } + free(namelist[i]); + } + free(namelist); + if(err) + return "Failed to include a file"; + return NULL; +} + +/**************************************** fatal error */ + +NOEXPORT void print_syntax(void) { + s_log(LOG_NOTICE, " "); + s_log(LOG_NOTICE, "Syntax:"); + s_log(LOG_NOTICE, "stunnel " +#ifdef USE_WIN32 +#ifndef _WIN32_WCE + "[ [-install | -uninstall | -start | -stop | -reload | -reopen | -exit] " +#endif + "[-quiet] " +#endif + "[] ] " +#ifndef USE_WIN32 + "-fd " +#endif + "| -help | -version | -sockets | -options"); + s_log(LOG_NOTICE, " - use specified config file"); +#ifdef USE_WIN32 +#ifndef _WIN32_WCE + s_log(LOG_NOTICE, " -install - install NT service"); + s_log(LOG_NOTICE, " -uninstall - uninstall NT service"); + s_log(LOG_NOTICE, " -start - start NT service"); + s_log(LOG_NOTICE, " -stop - stop NT service"); + s_log(LOG_NOTICE, " -reload - reload configuration for NT service"); + s_log(LOG_NOTICE, " -reopen - reopen log file for NT service"); + s_log(LOG_NOTICE, " -exit - exit an already started stunnel"); +#endif + s_log(LOG_NOTICE, " -quiet - don't display message boxes"); +#else + s_log(LOG_NOTICE, " -fd - read the config file from a file descriptor"); +#endif + s_log(LOG_NOTICE, " -help - get config file help"); + s_log(LOG_NOTICE, " -version - display version and defaults"); + s_log(LOG_NOTICE, " -sockets - display default socket options"); + s_log(LOG_NOTICE, " -options - display supported TLS options"); +} + +/**************************************** various supporting functions */ + +NOEXPORT void name_list_append(NAME_LIST **ptr, char *name) { + while(*ptr) /* find the null pointer */ + ptr=&(*ptr)->next; + *ptr=str_alloc_detached(sizeof(NAME_LIST)); + (*ptr)->name=str_dup_detached(name); + (*ptr)->next=NULL; +} + +NOEXPORT void name_list_dup(NAME_LIST **dst, NAME_LIST *src) { + for(; src; src=src->next) + name_list_append(dst, src->name); +} + +NOEXPORT void name_list_free(NAME_LIST *ptr) { + while(ptr) { + NAME_LIST *next=ptr->next; + str_free(ptr->name); + str_free(ptr); + ptr=next; + } +} + +#ifndef USE_WIN32 + +/* allocate 'exec' arguments */ +/* TODO: support quotes */ +NOEXPORT char **arg_alloc(char *str) { + size_t max_arg, i; + char **tmp, **retval; + + max_arg=strlen(str)/2+1; + tmp=str_alloc((max_arg+1)*sizeof(char *)); + + i=0; + while(*str && i