/*- * SSLsplit - transparent SSL/TLS interception * https://www.roe.ch/SSLsplit * * Copyright (c) 2009-2018, Daniel Roethlisberger . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "cert.h" #include "ssl.h" #include "easylogging++.h" #include /* * Certificate, including private key and certificate chain. */ cert_t * cert_new(void) { cert_t * c; if (!(c = (cert_t *) malloc(sizeof(cert_t)))) return NULL; memset(c, 0, sizeof(cert_t)); if (pthread_mutex_init(&c->mutex, NULL)) { free(c); return NULL; } c->references = 1; return c; } /* * Passed OpenSSL objects are owned by cert_t; refcount will not be * incremented, stack will not be duplicated. */ cert_t * cert_new3(EVP_PKEY * key, X509 * crt, STACK_OF(X509) * chain) { cert_t * c; if (!(c = (cert_t *) malloc(sizeof(cert_t)))) return NULL; if (pthread_mutex_init(&c->mutex, NULL)) { free(c); return NULL; } c->key = key; c->crt = crt; c->chain = chain; c->references = 1; return c; } /* * Passed OpenSSL objects are copied by cert_t; crt/key refcount will be * incremented, stack will be duplicated. */ cert_t * cert_new3_copy(EVP_PKEY * key, X509 * crt, STACK_OF(X509) * chain) { cert_t * c; if (!(c = (cert_t *) malloc(sizeof(cert_t)))) return NULL; if (pthread_mutex_init(&c->mutex, NULL)) { free(c); return NULL; } c->key = key; ssl_key_refcount_inc(c->key); c->crt = crt; ssl_x509_refcount_inc(c->crt); c->chain = sk_X509_dup(chain); for (int i = 0; i < sk_X509_num(c->chain); i++) { ssl_x509_refcount_inc(sk_X509_value(c->chain, i)); } c->references = 1; return c; } /* * Load cert_t from file. */ cert_t * cert_new_load(const char * filename) { cert_t * c; if (!(c = (cert_t *) malloc(sizeof(cert_t)))) return NULL; memset(c, 0, sizeof(cert_t)); if (pthread_mutex_init(&c->mutex, NULL)) { free(c); return NULL; } if (ssl_x509chain_load(&c->crt, &c->chain, filename) == -1) { free(c); return NULL; } c->key = ssl_key_load(filename); if (!c->key) { X509_free(c->crt); if (c->chain) { sk_X509_pop_free(c->chain, X509_free); } free(c); return NULL; } c->references = 1; return c; } /* * Increment reference count. */ void cert_refcount_inc(cert_t * c) { pthread_mutex_lock(&c->mutex); c->references++; pthread_mutex_unlock(&c->mutex); } /* * Thread-safe setter functions; they copy the value (refcounts are inc'd). */ void cert_set_key(cert_t * c, EVP_PKEY * key) { pthread_mutex_lock(&c->mutex); if (c->key) { EVP_PKEY_free(c->key); } c->key = key; if (c->key) { ssl_key_refcount_inc(c->key); } pthread_mutex_unlock(&c->mutex); } void cert_set_crt(cert_t * c, X509 * crt) { pthread_mutex_lock(&c->mutex); if (c->crt) { X509_free(c->crt); } c->crt = crt; if (c->crt) { ssl_x509_refcount_inc(c->crt); } pthread_mutex_unlock(&c->mutex); } void cert_set_chain(cert_t * c, STACK_OF(X509) * chain) { pthread_mutex_lock(&c->mutex); if (c->chain) { sk_X509_pop_free(c->chain, X509_free); } if (chain) { c->chain = sk_X509_dup(chain); for (int i = 0; i < sk_X509_num(c->chain); i++) { ssl_x509_refcount_inc(sk_X509_value(c->chain, i)); } } else { c->chain = NULL; } pthread_mutex_unlock(&c->mutex); } /* * Free cert including internal objects. */ void cert_free(cert_t * c) { pthread_mutex_lock(&c->mutex); c->references--; if (c->references) { pthread_mutex_unlock(&c->mutex); return; } pthread_mutex_unlock(&c->mutex); pthread_mutex_destroy(&c->mutex); if (c->key) { EVP_PKEY_free(c->key); } if (c->crt) { X509_free(c->crt); } if (c->chain) { sk_X509_pop_free(c->chain, X509_free); } free(c); } #include #include #include #include "cfgparser.h" void __throw_ssl_load_exception(int err, std::string what) { /* 系统错误 */ if (err) { throw std::system_error(err, std::generic_category(), what); } /* SSL错误 */ std::string ssl_err_info; ERR_print_errors_cb([](const char * str, size_t len, void * u) -> int { std::string * __str_err_info_ptr = static_cast(u); *__str_err_info_ptr = std::string(str, len); return 0; }, static_cast(&ssl_err_info)); throw std::runtime_error(what + ":" + ssl_err_info); } void __debug_dump_crt(const char * str_leader, X509 * crt) { LOG(INFO) << str_leader; char * sj = ssl_x509_subject(crt); if (sj) { LOG(INFO) << string_format("Subject DN: %s", sj); free(sj); } char * names = ssl_x509_names_to_str(crt); if (names) { LOG(INFO) << string_format("Common Names: %s", names); free(names); } char * fpr; if (!(fpr = ssl_x509_fingerprint(crt, 1))) { LOG(INFO) << string_format("Warning: Error generating X509 fingerprint"); } else { LOG(INFO) << string_format("Fingerprint: %s\n", fpr); free(fpr); } return; } int CertCA::__load_cakey_from_file(CertCA::ca_handlers * ca_config, const std::string & cakey_filepath) { if (ca_config->cakey) { EVP_PKEY_free(ca_config->cakey); } ca_config->cakey = ssl_key_load(cakey_filepath.c_str()); if (!ca_config->cakey) { __throw_ssl_load_exception(errno, string_format("Error loading CA keys from %s", cakey_filepath.c_str())); } if (!ca_config->cacrt) { ca_config->cacrt = ssl_x509_load(cakey_filepath.c_str()); if (ca_config->cacrt) { ssl_x509_refcount_inc(ca_config->cacrt); sk_X509_insert(ca_config->chain, ca_config->cacrt, 0); } } return 0; } int CertCA::__load_cacert_from_file(CertCA::ca_handlers * ca_config, const std::string & cacrt_filepath) { if (ca_config->cacrt) { X509_free(ca_config->cacrt); } ca_config->cacrt = ssl_x509_load(cacrt_filepath.c_str()); if (!ca_config->cacrt) { __throw_ssl_load_exception(errno, string_format("error loading CA cert from %s", cacrt_filepath.c_str())); } ssl_x509_refcount_inc(ca_config->cacrt); sk_X509_insert(ca_config->chain, ca_config->cacrt, 0); if (!ca_config->cakey) { ca_config->cakey = ssl_key_load(cacrt_filepath.c_str()); } return 0; } void CertCA::__throw_ssl_load_exception(int err, std::string what) { /* 系统错误 */ if (err) { throw std::system_error(err, std::generic_category(), what); } /* SSL错误 */ std::string ssl_err_info; ERR_print_errors_cb([](const char * str, size_t len, void * u) -> int { auto * __str_err_info_ptr = static_cast(u); *__str_err_info_ptr = std::string(str, len); return 0; }, static_cast(&ssl_err_info)); throw std::runtime_error(what + ":" + ssl_err_info); } std::unique_ptr CertCA::Factory(TfeConfigParser & cfg_parser) { /* 调用Private的构造函数 */ std::unique_ptr __this_object = std::unique_ptr(new CertCA); /* 加载可信根证书、不可信根证书证书、密钥 */ auto trust_ca_key_ret = cfg_parser.TryGetValue("ca", "trust_ca_key"); if (trust_ca_key_ret.first) { __load_cakey_from_file(&__this_object->trust_ca_handlers_, trust_ca_key_ret.second); } auto trust_ca_crt_ret = cfg_parser.TryGetValue("ca", "trust_ca_crt"); if (trust_ca_crt_ret.first) { __load_cacert_from_file(&__this_object->trust_ca_handlers_, trust_ca_crt_ret.second); } auto untrust_ca_key_ret = cfg_parser.TryGetValue("ca", "untrust_ca_key"); if (untrust_ca_key_ret.first) { __load_cakey_from_file(&__this_object->untrust_ca_handlers_, untrust_ca_key_ret.second); } auto untrust_ca_crt_ret = cfg_parser.TryGetValue("ca", "untrust_ca_crt"); if (untrust_ca_crt_ret.first) { __load_cacert_from_file(&__this_object->untrust_ca_handlers_, untrust_ca_crt_ret.second); } /* 根证书库,用于证书真伪认证 */ auto ca_root_filepath = cfg_parser.TryGetValue("ca", "root_ca_store_file"); if (ca_root_filepath.first) { __this_object->is_ca_root_filepath_set_ = true; __this_object->ca_root_filepath_ = ca_root_filepath.second; } auto ca_root_dirpath = cfg_parser.TryGetValue("ca", "root_ca_store_dir"); if (ca_root_dirpath.first) { __this_object->is_ca_root_dirpath_set_ = true; __this_object->ca_root_dirpath_ = ca_root_dirpath.second; } /* 加载根证书库 */ decltype(__this_object->x509_store_) x509_store{X509_STORE_new()}; if (unlikely(x509_store == nullptr)) { throw std::runtime_error("Failed at creating X509_STORE"); } int ret = X509_STORE_set_default_paths(x509_store.get()); if (unlikely(ret == 0)) { throw std::runtime_error("Failed at setting default paths for X509_STORE"); } ret = X509_STORE_load_locations(x509_store.get(), __this_object->is_ca_root_filepath_set_ ? __this_object->ca_root_filepath_.c_str() : nullptr, __this_object->is_ca_root_dirpath_set_ ? __this_object->ca_root_dirpath_.c_str() : nullptr); if (unlikely(ret == 0)) { throw std::runtime_error("Failed at setting load locations for X509_STORE"); } __this_object->x509_store_ = std::move(x509_store); LOG(INFO) << string_format("Trust CA Cert Location: %s", trust_ca_crt_ret.second.c_str()); __debug_dump_crt("Trust CA Cert Information: ", __this_object->trust_ca_handlers_.cacrt); LOG(INFO) << string_format("Unrust CA Cert Location: %s", untrust_ca_crt_ret.second.c_str()); __debug_dump_crt("Untrust CA Cert Information: ", __this_object->untrust_ca_handlers_.cacrt); return __this_object; } bool CertCA::SSLConnectionVerify(const SSL * ssl_connection) { STACK_OF(X509) * cert_chain = SSL_get_peer_cert_chain(ssl_connection); if (unlikely(cert_chain == nullptr)) { LOG(DEBUG) << string_format("No SSL cert chain found, ssl: %p", ssl_connection); return true; } std::unique_ptr x509_store_ctx{ X509_STORE_CTX_new()}; if (unlikely(x509_store_ctx == nullptr)) { throw std::runtime_error("Failed at creating X509_STORE_CTX"); } X509 * cert = sk_X509_value(cert_chain, 0); if (!X509_STORE_CTX_init(x509_store_ctx.get(), x509_store_.get(), cert, cert_chain)) { throw std::runtime_error("Failed at initializing X509_STORE_CTX"); } return X509_verify_cert(x509_store_ctx.get()) != 0; } /* vim: set noet ft=c: */