Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

ルート証明書の設定を統一してカスタマイズできるようにした #135

Merged
merged 4 commits into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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