Skip to content

Commit

Permalink
Merge pull request #135 from shiguredo/feature/custom-verify
Browse files Browse the repository at this point in the history
ルート証明書の設定を統一してカスタマイズできるようにした
  • Loading branch information
voluntas authored Mar 3, 2020
2 parents ba5fa85 + eb31b5b commit 3ae6674
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 11 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ target_sources(momo
src/momo_version.cpp
src/util.cpp
src/watchdog.cpp
src/ssl_verifier.cpp
src/ayame/ayame_server.cpp
src/ayame/ayame_websocket_client.cpp
src/p2p/p2p_connection.cpp
Expand Down
22 changes: 17 additions & 5 deletions src/ayame/ayame_websocket_client.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include "ayame_websocket_client.h"

// boost
#include <boost/beast/websocket/stream.hpp>

// json
#include <nlohmann/json.hpp>

#include "../momo_version.h"
#include "momo_version.h"
#include "ssl_verifier.h"
#include "url_parts.h"
#include "util.h"

Expand All @@ -28,7 +32,7 @@ bool AyameWebsocketClient::parseURL(URLParts& parts) const {

boost::asio::ssl::context AyameWebsocketClient::createSSLContext() const {
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
ctx.set_default_verify_paths();
//ctx.set_default_verify_paths();
ctx.set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
Expand Down Expand Up @@ -72,10 +76,18 @@ void AyameWebsocketClient::reset() {

if (parseURL(parts_)) {
auto ssl_ctx = createSSLContext();
boost::beast::websocket::stream<
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>
wss(ioc_, ssl_ctx);
ws_.reset(new Websocket(ioc_, std::move(ssl_ctx)));
ws_->nativeSecureSocket().next_layer().set_verify_mode(
boost::asio::ssl::verify_peer);
ws_->nativeSecureSocket().next_layer().set_verify_callback(
[](bool preverified, boost::asio::ssl::verify_context& ctx) {
if (preverified) {
return true;
}
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
return SSLVerifier::VerifyX509(cert);
});

// SNI の設定を行う
if (!SSL_set_tlsext_host_name(
ws_->nativeSecureSocket().next_layer().native_handle(),
Expand Down
24 changes: 22 additions & 2 deletions src/rtc/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "modules/video_capture/video_capture_factory.h"
#include "observer.h"
#include "rtc_base/logging.h"
#include "rtc_base/openssl_certificate.h"
#include "rtc_base/ssl_adapter.h"
#include "scalable_track_source.h"
#include "util.h"
Expand All @@ -41,6 +42,8 @@
#include "hw_video_decoder_factory.h"
#endif

#include "ssl_verifier.h"

RTCManager::RTCManager(
ConnectionSettings conn_settings,
rtc::scoped_refptr<ScalableVideoTrackSource> video_track_source,
Expand Down Expand Up @@ -191,16 +194,33 @@ void RTCManager::SetDataManager(RTCDataManager* data_manager) {
_data_manager = data_manager;
}

class RTCSSLVerifier : public rtc::SSLCertificateVerifier {
public:
RTCSSLVerifier() {}
bool Verify(const rtc::SSLCertificate& certificate) override {
return SSLVerifier::VerifyX509(
static_cast<const rtc::OpenSSLCertificate&>(certificate).x509());
}
};

std::shared_ptr<RTCConnection> RTCManager::createConnection(
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config,
RTCMessageSender* sender) {
rtc_config.enable_dtls_srtp = true;
rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
std::unique_ptr<PeerConnectionObserver> observer(
new PeerConnectionObserver(sender, _receiver, _data_manager));
webrtc::PeerConnectionDependencies dependencies(observer.get());

// WebRTC の SSL 接続の検証は自前のルート証明書(rtc_base/ssl_roots.h)でやっていて、
// その中に Let's Encrypt の証明書が無いため、接続先によっては接続できないことがある。
//
// それを解消するために tls_cert_verifier を設定して自前で検証を行う。
dependencies.tls_cert_verifier =
std::unique_ptr<rtc::SSLCertificateVerifier>(new RTCSSLVerifier());

rtc::scoped_refptr<webrtc::PeerConnectionInterface> connection =
_factory->CreatePeerConnection(rtc_config, nullptr, nullptr,
observer.get());
_factory->CreatePeerConnection(rtc_config, std::move(dependencies));
if (!connection) {
RTC_LOG(LS_ERROR) << __FUNCTION__ << ": CreatePeerConnection failed";
return nullptr;
Expand Down
17 changes: 13 additions & 4 deletions src/sora/sora_websocket_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <nlohmann/json.hpp>

#include "momo_version.h"
#include "ssl_verifier.h"
#include "url_parts.h"
#include "util.h"

Expand All @@ -34,7 +35,7 @@ bool SoraWebsocketClient::parseURL(URLParts& parts) const {

boost::asio::ssl::context SoraWebsocketClient::createSSLContext() const {
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
ctx.set_default_verify_paths();
//ctx.set_default_verify_paths();
ctx.set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
Expand Down Expand Up @@ -74,10 +75,18 @@ void SoraWebsocketClient::reset() {

if (parseURL(parts_)) {
auto ssl_ctx = createSSLContext();
boost::beast::websocket::stream<
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>
wss(ioc_, ssl_ctx);
ws_.reset(new Websocket(ioc_, std::move(ssl_ctx)));
ws_->nativeSecureSocket().next_layer().set_verify_mode(
boost::asio::ssl::verify_peer);
ws_->nativeSecureSocket().next_layer().set_verify_callback(
[](bool preverified, boost::asio::ssl::verify_context& ctx) {
if (preverified) {
return true;
}
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
return SSLVerifier::VerifyX509(cert);
});

// SNI の設定を行う
if (!SSL_set_tlsext_host_name(
ws_->nativeSecureSocket().next_layer().native_handle(),
Expand Down
115 changes: 115 additions & 0 deletions src/ssl_verifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "ssl_verifier.h"

// webrtc
#include <rtc_base/logging.h>
#include <rtc_base/openssl_utility.h>
#include <rtc_base/ssl_roots.h>

// openssl
#include <openssl/x509v3.h>

// boost
#include <boost/asio/connect.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>

bool SSLVerifier::AddCert(const std::string& pem, X509_STORE* store) {
BIO* bio = BIO_new_mem_buf(pem.c_str(), pem.size());
if (bio == nullptr) {
RTC_LOG(LS_ERROR) << "BIO_new_mem_buf failed";
return false;
}
X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
if (cert == nullptr) {
BIO_free(bio);
RTC_LOG(LS_ERROR) << "PEM_read_bio_X509 failed";
return false;
}
int r = X509_STORE_add_cert(store, cert);
if (r == 0) {
X509_free(cert);
BIO_free(bio);
RTC_LOG(LS_ERROR) << "X509_STORE_add_cert failed";
return false;
}
X509_free(cert);
BIO_free(bio);
return true;
}

// rtc_base/openssl_utility.cc を今回の用途に合わせたもの
bool SSLVerifier::LoadBuiltinSSLRootCertificates(X509_STORE* store) {
int count_of_added_certs = 0;
for (size_t i = 0;
i < sizeof(kSSLCertCertificateList) / sizeof(kSSLCertCertificateList[0]);
i++) {
const unsigned char* cert_buffer = kSSLCertCertificateList[i];
size_t cert_buffer_len = kSSLCertCertificateSizeList[i];
X509* cert = d2i_X509(nullptr, &cert_buffer,
cert_buffer_len); // NOLINT
if (cert) {
int return_value = X509_STORE_add_cert(store, cert);
if (return_value == 0) {
RTC_LOG(LS_WARNING) << "Unable to add certificate.";
} else {
count_of_added_certs++;
}
X509_free(cert);
}
}
return count_of_added_certs > 0;
}

bool SSLVerifier::VerifyX509(X509* x509) {
{
char subject_name[256];
X509_NAME_oneline(X509_get_subject_name(x509), subject_name, 256);
RTC_LOG(LS_INFO) << "Verifying " << subject_name;
}

X509_STORE* store = nullptr;
X509_STORE_CTX* ctx = nullptr;

struct Guard {
std::function<void()> f;
Guard(std::function<void()> f) : f(std::move(f)) {}
~Guard() { f(); }
};
Guard guard([&]() {
// nullptr を渡しても何もしない
X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
});

store = X509_STORE_new();
if (store == nullptr) {
RTC_LOG(LS_ERROR) << "X509_STORE_new failed";
return false;
}

// WebRTC が用意しているルート証明書の設定
LoadBuiltinSSLRootCertificates(store);
// デフォルト証明書のパスの設定
X509_STORE_set_default_paths(store);
RTC_LOG(LS_INFO) << "default cert file: " << X509_get_default_cert_file();

ctx = X509_STORE_CTX_new();
if (ctx == nullptr) {
RTC_LOG(LS_ERROR) << "X509_STORE_CTX_new failed";
return false;
}
int r;
r = X509_STORE_CTX_init(ctx, store, x509, nullptr);
if (r == 0) {
RTC_LOG(LS_ERROR) << "X509_STORE_CTX_init failed";
return false;
}
r = X509_verify_cert(ctx);
if (r <= 0) {
RTC_LOG(LS_INFO) << "X509_verify_cert failed: r=" << r << " message="
<< X509_verify_cert_error_string(
X509_STORE_CTX_get_error(ctx));
return false;
}
return true;
}
21 changes: 21 additions & 0 deletions src/ssl_verifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef SSL_VERIFIER_H_INCLUDED
#define SSL_VERIFIER_H_INCLUDED

#include <string>

// openssl
#include <openssl/ssl.h>

// 自前で SSL の証明書検証を行うためのクラス
class SSLVerifier {
public:
static bool VerifyX509(X509* x509);

private:
// PEM 形式のルート証明書を追加する
static bool AddCert(const std::string& pem, X509_STORE* store);
// WebRTC の組み込みルート証明書を追加する
static bool LoadBuiltinSSLRootCertificates(X509_STORE* store);
};

#endif // SSL_VERIFIER_H_INCLUDED

0 comments on commit 3ae6674

Please # to comment.