mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-28 17:57:05 +02:00
Verify server certificate
This commit is contained in:
parent
3d5744c6b0
commit
b20bd90c03
5 changed files with 77 additions and 19 deletions
|
@ -497,6 +497,27 @@ void ClientConnectionWs::write(boost::asio::streambuf& buffer, WriteHandler&& wr
|
|||
ClientConnectionWss::ClientConnectionWss(boost::asio::io_context& io_context, boost::asio::ssl::context& ssl_context, ClientSettings::Server server)
|
||||
: ClientConnection(io_context, std::move(server)), ssl_ws_(strand_, ssl_context)
|
||||
{
|
||||
if (server.certificate.has_value())
|
||||
{
|
||||
ssl_ws_.next_layer().set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
ssl_ws_.next_layer().set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx)
|
||||
{
|
||||
// The verify callback can be used to check whether the certificate that is
|
||||
// being presented is valid for the peer. For example, RFC 2818 describes
|
||||
// the steps involved in doing this for HTTPS. Consult the OpenSSL
|
||||
// documentation for more details. Note that the callback is called once
|
||||
// for each certificate in the certificate chain, starting from the root
|
||||
// certificate authority.
|
||||
|
||||
// In this example we will simply print the certificate's subject name.
|
||||
char subject_name[256];
|
||||
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
||||
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
|
||||
LOG(INFO, LOG_TAG) << "verifying cert: '" << subject_name << "', pre verified: " << preverified << "\n";
|
||||
|
||||
return preverified;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "player/pcm_device.hpp"
|
||||
|
||||
// standard headers
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
@ -64,6 +66,13 @@ struct ClientSettings
|
|||
std::string protocol;
|
||||
/// server port
|
||||
size_t port{1704};
|
||||
/// server certificate
|
||||
std::optional<std::filesystem::path> certificate;
|
||||
/// Is ssl in use?
|
||||
bool isSsl() const
|
||||
{
|
||||
return (protocol == "wss");
|
||||
}
|
||||
};
|
||||
|
||||
/// The audio player (DAC)
|
||||
|
|
|
@ -80,8 +80,19 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings
|
|||
: io_context_(io_context), ssl_context_(boost::asio::ssl::context::tlsv12_client), timer_(io_context), settings_(settings), stream_(nullptr),
|
||||
decoder_(nullptr), player_(nullptr), serverSettings_(nullptr)
|
||||
{
|
||||
// TODO: Load and verify certificate
|
||||
// ssl_context_.load_verify_file("/home/johannes/Develop/snapcast/server/etc/certs/snapcastCA.crt");
|
||||
if (settings.server.isSsl() && settings.server.certificate.has_value())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
ssl_context_.set_default_verify_paths(ec);
|
||||
if (ec.failed())
|
||||
LOG(WARNING, LOG_TAG) << "Failed to load system certificates: " << ec << "\n";
|
||||
if (!settings.server.certificate->empty())
|
||||
{
|
||||
ssl_context_.load_verify_file(settings.server.certificate.value(), ec);
|
||||
if (ec.failed())
|
||||
throw SnapException("Failed to load certificate: " + settings.server.certificate.value().native() + ": " + ec.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <boost/asio/signal_set.hpp>
|
||||
|
||||
// standard headers
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#ifndef WINDOWS
|
||||
#include <csignal>
|
||||
|
@ -135,23 +136,27 @@ int main(int argc, char** argv)
|
|||
ClientSettings settings;
|
||||
string pcm_device(player::DEFAULT_DEVICE);
|
||||
|
||||
OptionParser op("Allowed options");
|
||||
auto helpSwitch = op.add<Switch>("", "help", "produce help message");
|
||||
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message");
|
||||
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
|
||||
op.add<Value<string>>("h", "host", "server hostname or ip address", "", &settings.server.host);
|
||||
op.add<Value<size_t>>("p", "port", "server port", 1704, &settings.server.port);
|
||||
op.add<Value<size_t>>("i", "instance", "instance id when running multiple instances on the same host", 1, &settings.instance);
|
||||
op.add<Value<string>>("", "hostID", "unique host id, default is MAC address", "", &settings.host_id);
|
||||
OptionParser op("Usage: snapclient [options...] [url]\n\n"
|
||||
" With 'url' = <tcp|ws|wss>://<snapserver host or IP>[:port]\n"
|
||||
" For example: \"tcp:\\\\192.168.1.1:1704\", or \"wss:\\\\homeserver.local\"\n"
|
||||
" If 'url' is not configured, snapclient tries to resolve the snapserver IP via mDNS\n");
|
||||
auto helpSwitch = op.add<Switch>("", "help", "Produce help message");
|
||||
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "Produce groff message");
|
||||
auto versionSwitch = op.add<Switch>("v", "version", "Show version number");
|
||||
op.add<Value<string>>("h", "host", "(deprecated, use [url]) Server hostname or ip address", "", &settings.server.host);
|
||||
op.add<Value<size_t>>("p", "port", "(deprecated, use [url]) Server port", 1704, &settings.server.port);
|
||||
op.add<Value<size_t>>("i", "instance", "Instance id when running multiple instances on the same host", 1, &settings.instance);
|
||||
op.add<Value<string>>("", "hostID", "Unique host id, default is MAC address", "", &settings.host_id);
|
||||
auto server_cert_opt = op.add<Implicit<std::filesystem::path>>("", "server-cert", "Verify server with certificate", "default certificates");
|
||||
|
||||
// PCM device specific
|
||||
#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI)
|
||||
auto listSwitch = op.add<Switch>("l", "list", "list PCM devices");
|
||||
/*auto soundcardValue =*/op.add<Value<string>>("s", "soundcard", "index or name of the pcm device", pcm_device, &pcm_device);
|
||||
auto listSwitch = op.add<Switch>("l", "list", "List PCM devices");
|
||||
/*auto soundcardValue =*/op.add<Value<string>>("s", "Soundcard", "index or name of the pcm device", pcm_device, &pcm_device);
|
||||
#endif
|
||||
/*auto latencyValue =*/op.add<Value<int>>("", "latency", "latency of the PCM device", 0, &settings.player.latency);
|
||||
/*auto latencyValue =*/op.add<Value<int>>("", "Latency", "latency of the PCM device", 0, &settings.player.latency);
|
||||
#ifdef HAS_SOXR
|
||||
auto sample_format = op.add<Value<string>>("", "sampleformat", "resample audio stream to <rate>:<bits>:<channels>", "");
|
||||
auto sample_format = op.add<Value<string>>("", "sampleformat", "Resample audio stream to <rate>:<bits>:<channels>", "");
|
||||
#endif
|
||||
|
||||
auto supported_players = Controller::getSupportedPlayerNames();
|
||||
|
@ -162,7 +167,7 @@ int main(int argc, char** argv)
|
|||
|
||||
// sharing mode
|
||||
#if defined(HAS_OBOE) || defined(HAS_WASAPI)
|
||||
auto sharing_mode = op.add<Value<string>>("", "sharingmode", "audio mode to use [shared|exclusive]", "shared");
|
||||
auto sharing_mode = op.add<Value<string>>("", "sharingmode", "Audio mode to use [shared|exclusive]", "shared");
|
||||
#endif
|
||||
|
||||
// mixer
|
||||
|
@ -183,12 +188,12 @@ int main(int argc, char** argv)
|
|||
// daemon settings
|
||||
#ifdef HAS_DAEMON
|
||||
int processPriority(-3);
|
||||
auto daemonOption = op.add<Implicit<int>>("d", "daemon", "daemonize, optional process priority [-20..19]", processPriority, &processPriority);
|
||||
auto daemonOption = op.add<Implicit<int>>("d", "daemon", "Daemonize, optional process priority [-20..19]", processPriority, &processPriority);
|
||||
auto userValue = op.add<Value<string>>("", "user", "the user[:group] to run snapclient as when daemonized");
|
||||
#endif
|
||||
|
||||
// logging
|
||||
op.add<Value<string>>("", "logsink", "log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
|
||||
op.add<Value<string>>("", "logsink", "Log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
|
||||
auto logfilterOption = op.add<Value<string>>(
|
||||
"", "logfilter", "log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
|
||||
settings.logging.filter);
|
||||
|
@ -318,6 +323,18 @@ int main(int argc, char** argv)
|
|||
settings.server.port = 1788;
|
||||
}
|
||||
|
||||
if (server_cert_opt->is_set())
|
||||
{
|
||||
if (server_cert_opt->get_default() == server_cert_opt->value())
|
||||
settings.server.certificate = "";
|
||||
else
|
||||
settings.server.certificate = std::filesystem::weakly_canonical(server_cert_opt->value());
|
||||
if (settings.server.certificate.value_or("").empty())
|
||||
LOG(INFO, LOG_TAG) << "Server certificate: default certificates\n";
|
||||
else
|
||||
LOG(INFO, LOG_TAG) << "Server certificate: " << settings.server.certificate.value_or("") << "\n";
|
||||
}
|
||||
|
||||
#if !defined(HAS_AVAHI) && !defined(HAS_BONJOUR)
|
||||
if (settings.server.host.empty())
|
||||
throw SnapException("Snapserver host not configured and mDNS not available, please configure with \"--host\".");
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
( _ \ / \( _ \( )
|
||||
) __/( O )) __// (_/\
|
||||
(__) \__/(__) \____/
|
||||
version 1.3.0
|
||||
version 1.3.1
|
||||
https://github.com/badaix/popl
|
||||
|
||||
This file is part of popl (program options parser lib)
|
||||
|
@ -1167,7 +1167,7 @@ inline std::string ConsoleOptionPrinter::print(const Attribute& max_attribute) c
|
|||
|
||||
std::stringstream s;
|
||||
if (!option_parser_->description().empty())
|
||||
s << option_parser_->description() << ":\n";
|
||||
s << option_parser_->description() << "\n";
|
||||
|
||||
size_t optionRightMargin(20);
|
||||
const size_t maxDescriptionLeftMargin(40);
|
||||
|
|
Loading…
Add table
Reference in a new issue