diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 637f2704..6f03cb45 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -12,7 +12,7 @@ if (RDMACM_LIBRARIES) set(MSGR_RDMACM "msgr_rdmacm.cpp") endif (RDMACM_LIBRARIES) add_library(vitastor_common STATIC - ../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c + ../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp msgr_encrypt.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM} ) @@ -99,7 +99,7 @@ add_executable(test_cluster_client EXCLUDE_FROM_ALL ../test/test_cluster_client.cpp pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp cluster_client_icache.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp msgr_encrypt.cpp - etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../../json11/json11.cpp + etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp ../../json11/json11.cpp ) target_link_libraries(test_cluster_client ${LIBURING_LIBRARIES} ${OPENSSL_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES}) target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__) diff --git a/src/client/http_client.cpp b/src/client/http_client.cpp index c9554e2a..5140db71 100644 --- a/src/client/http_client.cpp +++ b/src/client/http_client.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "openssl_util.h" #endif // libc-ares @@ -163,105 +164,6 @@ void http_ares_cb(void *data, ares_socket_t socket_fd, int readable, int writabl }); } -#ifdef WITH_OPENSSL -bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem) -{ - std::string pem; - BIO *bio = NULL; - if (file_or_pem.substr(0, 5) != "-----") - { - pem = read_file(file_or_pem); - bio = BIO_new_mem_buf(pem.data(), pem.size()); - } - else - bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size()); - if (!bio) - return false; - X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); - bool ok = !!x509; - if (x509) - { - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); - X509_STORE_add_cert(store, x509); - X509_free(x509); - } - BIO_free(bio); - return ok; -} - -bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem) -{ - if (file_or_pem.substr(0, 5) == "-----") - { - return openssl_ctx_add_ca(ssl_ctx, file_or_pem); - } - return file_or_pem.empty() - ? !!SSL_CTX_set_default_verify_paths(ssl_ctx) - : !!SSL_CTX_load_verify_locations(ssl_ctx, file_or_pem.c_str(), NULL); -} - -std::string openssl_get_cn(X509 *x509) -{ - X509_NAME* subj = X509_get_subject_name(x509); - int pos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); - if (pos != -1) - { - X509_NAME_ENTRY* cn = X509_NAME_get_entry(subj, pos); - ASN1_STRING* str = X509_NAME_ENTRY_get_data(cn); - return std::string((const char*)ASN1_STRING_get0_data(str), ASN1_STRING_length(str)); - } - return ""; -} - -bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name) -{ - BIO *bio = NULL; - std::string contents; - if (file_or_pem.substr(0, 5) == "-----") - bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size()); - else - { - contents = read_file(file_or_pem); - if (!contents.size()) - return false; - bio = BIO_new_mem_buf(contents.data(), contents.size()); - } - if (!bio) - return false; - X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); - bool ok = !!x509; - if (x509) - { - ok = SSL_CTX_use_certificate(ssl_ctx, x509); - if (ok) - common_name = openssl_get_cn(x509); - X509_free(x509); - } - BIO_free(bio); - return ok; -} - -bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem) -{ - if (file_or_pem.substr(0, 5) == "-----") - { - BIO *bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size()); - if (!bio) - return false; - EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - bool ok = !!pkey; - if (pkey) - { - ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey); - EVP_PKEY_free(pkey); - } - BIO_free(bio); - return ok; - } - return !!SSL_CTX_use_PrivateKey_file(ssl_ctx, file_or_pem.c_str(), SSL_FILETYPE_PEM); -} -#endif - http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ssl_cert, const std::string & ssl_key, const std::string & ssl_ca, bool verify_peer, std::string & error) { diff --git a/src/client/http_client.h b/src/client/http_client.h index 464fb2c5..fc663698 100644 --- a/src/client/http_client.h +++ b/src/client/http_client.h @@ -8,10 +8,6 @@ #include #include "json11/json11.hpp" -#ifdef WITH_OPENSSL -#include -#endif - #define WS_CONTINUATION 0 #define WS_TEXT 1 #define WS_BINARY 2 @@ -73,11 +69,3 @@ void http_close(http_co_t *co); void http_destroy(http_co_t *co); #pragma GCC visibility pop - -#ifdef WITH_OPENSSL -bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem); -bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem); -std::string openssl_get_cn(X509 *x509); -bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name); -bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem); -#endif diff --git a/src/client/messenger.cpp b/src/client/messenger.cpp index 0b5883a4..67c64525 100644 --- a/src/client/messenger.cpp +++ b/src/client/messenger.cpp @@ -15,52 +15,10 @@ #ifdef WITH_RDMA #include "msgr_rdma.h" #endif -#include "http_client.h" -#include -#include -#include -#include void osd_messenger_t::init() { - if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty()) - { - // Initialize TLS context - if (tls_cert.empty() || tls_key.empty() || osd_tls_ca.empty() || osd_num && client_tls_ca.empty()) - { - if (osd_num) - fprintf(stderr, "Vitastor OSD TLS requires osd_tls_cert, osd_tls_key, osd_tls_ca, client_tls_ca\n"); - else - fprintf(stderr, "Vitastor client TLS requires tls_cert, tls_key and osd_tls_ca\n"); - exit(1); - } - else - { - ssl_ctx = SSL_CTX_new(TLS_method()); - if (!ssl_ctx) - { -init_err: - fprintf(stderr, "OpenSSL initialization failed: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); - } - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - bool ok = SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); - ok = ok && openssl_ctx_add_ca(ssl_ctx, osd_tls_ca); - if (osd_num) - { - // OSD uses 2 separate root certificates to distinguish between clients and peer OSDs - ok = ok && openssl_ctx_add_ca(ssl_ctx, client_tls_ca); - } - ok = ok && openssl_ctx_use_cert(ssl_ctx, tls_cert, tls_cn); - ok = ok && openssl_ctx_use_key(ssl_ctx, tls_key); - if (!ok) - { - SSL_CTX_free(ssl_ctx); - ssl_ctx = NULL; - goto init_err; - } - } - } + init_tls(); #ifdef WITH_RDMACM if (use_rdmacm) { @@ -236,30 +194,7 @@ osd_messenger_t::~osd_messenger_t() { destroy_aes_xts_decrypt(decrypt_ctx); } -#ifdef WITH_ISAL_CRYPTO - for (isal_gcm_context_data *ctx: encrypt_gcm_pool) - { - free(ctx); - } - for (isal_gcm_context_data *ctx: decrypt_gcm_pool) - { - free(ctx); - } -#else - for (EVP_CIPHER_CTX *ctx: encrypt_gcm_pool) - { - EVP_CIPHER_CTX_free(ctx); - } - for (EVP_CIPHER_CTX *ctx: decrypt_gcm_pool) - { - EVP_CIPHER_CTX_free(ctx); - } -#endif - if (ssl_ctx) - { - SSL_CTX_free(ssl_ctx); - ssl_ctx = NULL; - } + destroy_tls(); } void osd_messenger_t::parse_config(const json11::Json & config) @@ -557,7 +492,7 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd) handle_peer_epoll(peer_fd, epoll_events); }); // Check OSD number - ssl_init(cl, false); + init_tls_client(cl); check_peer_config(cl); } @@ -799,7 +734,7 @@ void osd_messenger_t::accept_connections(int listen_fd) cl->peer_fd = peer_fd; cl->peer_state = PEER_CONNECTED; cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size); - ssl_init(cl, true); + init_tls_client(cl); // Add FD to epoll tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events) { @@ -814,36 +749,6 @@ void osd_messenger_t::accept_connections(int listen_fd) } } -void osd_messenger_t::ssl_init(osd_client_t *cl, bool server_mode) -{ - if (!tls_cert.empty()) - { - cl->write_to_ssl = BIO_new(BIO_s_mem()); - cl->read_from_ssl = BIO_new(BIO_s_mem()); - cl->ssl_cli = SSL_new(ssl_ctx); - if (!cl->ssl_cli) - { - fprintf(stderr, "OpenSSL initialization failed: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); - } - if (server_mode) - { - SSL_set_accept_state(cl->ssl_cli); - } - else - { - SSL_set_connect_state(cl->ssl_cli); - } - SSL_set_bio(cl->ssl_cli, cl->write_to_ssl, cl->read_from_ssl); - bool ok = ssl_do_handshake(cl); - assert(ok); - } - else if (!test_osd_aes_key.empty()) - { - cl->gcm_enabled = true; - } -} - #ifdef WITH_RDMA msgr_rdma_context_t* osd_messenger_t::choose_rdma_context(osd_client_t *cl) { diff --git a/src/client/messenger.h b/src/client/messenger.h index f34463be..4b8b1be0 100644 --- a/src/client/messenger.h +++ b/src/client/messenger.h @@ -252,10 +252,15 @@ protected: #endif SSL_CTX *ssl_ctx = NULL; + X509 *tls_cert_obj = NULL; + X509 *osd_tls_ca_obj = NULL; + X509 *client_tls_ca_obj = NULL; std::string tls_cn; - void ssl_init(osd_client_t *cl, bool server_mode); - bool ssl_do_handshake(osd_client_t *cl); + void init_tls(); + void destroy_tls(); + void init_tls_client(osd_client_t *cl); + bool do_tls_handshake(osd_client_t *cl, bool from_recv = false); std::vector iothreads; std::vector read_ready_clients; diff --git a/src/client/msgr_encrypt.cpp b/src/client/msgr_encrypt.cpp index d18d1717..cf77c2c4 100644 --- a/src/client/msgr_encrypt.cpp +++ b/src/client/msgr_encrypt.cpp @@ -7,9 +7,15 @@ #include #endif +#include "str_util.h" #include "etcd_state_client.h" #include "messenger.h" #include "msgr_encrypt.h" +#include "http_client.h" +#include "openssl_util.h" + +#include +#include op_aes_xts_encrypt_t::op_aes_xts_encrypt_t() { @@ -479,3 +485,142 @@ void osd_messenger_t::op_encrypt_free(osd_client_t* cl) cl->xts_enc_ctx = NULL; } } + +void osd_messenger_t::init_tls() +{ + if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty()) + { + // Initialize TLS context + if (tls_cert.empty() || tls_key.empty() || osd_tls_ca.empty() || osd_num && client_tls_ca.empty()) + { + if (osd_num) + fprintf(stderr, "Vitastor OSD TLS requires osd_tls_cert, osd_tls_key, osd_tls_ca, client_tls_ca\n"); + else + fprintf(stderr, "Vitastor client TLS requires tls_cert, tls_key and osd_tls_ca\n"); + exit(1); + } + else + { + ssl_ctx = SSL_CTX_new(TLS_method()); + if (!ssl_ctx) + { +init_err: + fprintf(stderr, "OpenSSL initialization failed: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + // Always use TLS 1.3 with AES-256-GCM + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_ciphersuites(ssl_ctx, "TLS_AES_256_GCM_SHA384"); + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + bool ok = SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + ok = ok && (osd_tls_ca_obj = openssl_load_cert(osd_tls_ca)); + ok = ok && X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), osd_tls_ca_obj); + if (osd_num) + { + // OSD uses 2 separate root certificates to distinguish between clients and peer OSDs + ok = ok && (client_tls_ca_obj = openssl_load_cert(client_tls_ca)); + ok = ok && X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), client_tls_ca_obj); + } + ok = ok && openssl_ctx_use_cert(ssl_ctx, tls_cert, tls_cn); + ok = ok && openssl_ctx_use_key(ssl_ctx, tls_key); + if (!ok) + { + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + goto init_err; + } + } + } +} + +void osd_messenger_t::init_tls_client(osd_client_t *cl) +{ + if (!tls_cert.empty()) + { + cl->write_to_ssl = BIO_new(BIO_s_mem()); + cl->read_from_ssl = BIO_new(BIO_s_mem()); + cl->ssl_cli = SSL_new(ssl_ctx); + if (!cl->ssl_cli) + { + fprintf(stderr, "OpenSSL initialization failed: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + if (cl->is_incoming) + { + SSL_set_accept_state(cl->ssl_cli); + } + else + { + SSL_set_connect_state(cl->ssl_cli); + } + SSL_set_bio(cl->ssl_cli, cl->write_to_ssl, cl->read_from_ssl); + bool ok = do_tls_handshake(cl); + assert(ok); + } +} + +bool osd_messenger_t::do_tls_handshake(osd_client_t *cl, bool from_recv) +{ + if (cl->ssl_handshake_done) + return true; + int r = SSL_do_handshake(cl->ssl_cli); + if (r > 0) + { + cl->ssl_handshake_done = true; + } + else + { + r = SSL_get_error(cl->ssl_cli, r); + if (r != 0 && r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) + { + fprintf(stderr, "Client %ju TLS handshake error: %s, stopping client\n", cl->client_id, ERR_error_string(ERR_get_error(), NULL)); + cl->io_error = true; + return false; + } + } + if (from_recv && cl->write_state == 0 && openssl_bio_nonempty(cl->read_from_ssl)) + { + cl->write_state = CL_WRITE_READY; + write_ready_clients.push_back(cl->client_id); + } + return true; +} + +void osd_messenger_t::destroy_tls() +{ +#ifdef WITH_ISAL_CRYPTO + for (isal_gcm_context_data *ctx: encrypt_gcm_pool) + { + free(ctx); + } + for (isal_gcm_context_data *ctx: decrypt_gcm_pool) + { + free(ctx); + } +#else + for (EVP_CIPHER_CTX *ctx: encrypt_gcm_pool) + { + EVP_CIPHER_CTX_free(ctx); + } + for (EVP_CIPHER_CTX *ctx: decrypt_gcm_pool) + { + EVP_CIPHER_CTX_free(ctx); + } +#endif + if (osd_tls_ca_obj) + { + X509_free(osd_tls_ca_obj); + osd_tls_ca_obj = NULL; + } + if (client_tls_ca_obj) + { + X509_free(client_tls_ca_obj); + client_tls_ca_obj = NULL; + } + if (ssl_ctx) + { + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + } +} diff --git a/src/client/msgr_rdmacm.cpp b/src/client/msgr_rdmacm.cpp index 46ffd4cd..ad3d84ab 100644 --- a/src/client/msgr_rdmacm.cpp +++ b/src/client/msgr_rdmacm.cpp @@ -19,6 +19,7 @@ struct rdmacm_connecting_t int tcp_port = 0; int timeout_ms = 0; int timeout_id = -1; + bool is_incoming = false; msgr_rdma_context_t *rdma_context = NULL; }; @@ -292,6 +293,7 @@ void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev) conn->client_id = next_client_id++; conn->parsed_addr = *(sockaddr_storage*)rdma_get_peer_addr(ev->id); conn->rdma_context = rdma_context; + conn->is_incoming = true; rdmacm_set_conn_timeout(conn); rdmacm_connecting[ev->id] = conn; fprintf(stderr, "[OSD %ju] new client %ju: connection from %s via RDMA-CM\n", this->osd_num, conn->client_id, @@ -492,11 +494,13 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev) cl->peer_addr = conn->parsed_addr; cl->peer_port = conn->rdmacm_port; cl->client_id = conn->client_id; + cl->is_incoming = conn->is_incoming; cl->peer_state = PEER_RDMA; cl->connect_timeout_id = -1; cl->osd_num = peer_osd; cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size); cl->rdma_conn = rc; + init_tls_client(cl); clients[conn->client_id] = cl; if (conn->timeout_id >= 0) { diff --git a/src/client/msgr_receive.cpp b/src/client/msgr_receive.cpp index 0e1b7494..7a5adb2d 100644 --- a/src/client/msgr_receive.cpp +++ b/src/client/msgr_receive.cpp @@ -5,13 +5,14 @@ #include #include "messenger.h" #include "msgr_iothread.h" +#include "openssl_util.h" #include #include #include #include -#define RDR_TLS 1 +#define RDR_GCM 1 #define RDR_XTS 2 #define RDR_NO_CSUM 4 @@ -167,7 +168,7 @@ public: if (done >= bufsize) return false; size_t n = dst_len-from; - if (!(flags & RDR_TLS) || !cl->ssl_cli) + if (!(flags & RDR_GCM) || !cl->ssl_cli) { if (n > bufsize-done) n = bufsize-done; @@ -203,20 +204,8 @@ buffer_again: buffer_encrypted(); if (!cl->ssl_handshake_done) { - if (!msgr->ssl_do_handshake(cl)) + if (!msgr->do_tls_handshake(cl, true)) return false; - if (cl->write_state == 0) - { - // SSL_ERROR_WANT_WRITE is absolutely non-informative with memory BIO, it basically never happens - // So we have to check memory BIO for outstanding data - char *bio_buf = NULL; - size_t bio_sz = BIO_get_mem_data(cl->read_from_ssl, &bio_buf); - if (bio_sz > 0) - { - cl->write_state = CL_WRITE_READY; - msgr->write_ready_clients.push_back(cl->client_id); - } - } } int ok = SSL_read_ex(cl->ssl_cli, dst+from, n, &n); if (!ok) @@ -340,7 +329,7 @@ public: if (done >= bufsize) return false; size_t n = dst_len-from; - if (!(flags & RDR_TLS)) + if (!(flags & RDR_GCM)) { if (n > bufsize-done) n = bufsize-done; @@ -529,17 +518,15 @@ public: bool read(uint8_t *dst, size_t dst_len, int flags) override { - if (cl->gcm_enabled) - return false; // FIXME Only for tests, use copy-only with AES if (from >= dst_len) { // Skip from -= dst_len; return true; } - if ((flags & RDR_TLS) && cl->ssl_cli) + if ((flags & RDR_GCM) && (cl->ssl_cli || cl->gcm_enabled)) { - // Can't inplace read TLS data + // Can't inplace read TLS/GCM data return false; } if (cl->recv_list.size() >= IOV_MAX) @@ -759,7 +746,11 @@ void osd_messenger_t::handle_immediate_ops() bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize) { - if (cl->gcm_enabled) + if (cl->ssl_cli) + { + return handle_buffer_with(cl, curbuf, bufsize); + } + else if (cl->gcm_enabled) { return handle_buffer_with(cl, curbuf, bufsize); } @@ -983,7 +974,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr) bool hdr = (cl->read_op_pos < OSD_PACKET_SIZE); if (hdr || op->op_type == OSD_OP_IN) { - if (!rdr.read(op->req.buf, OSD_PACKET_SIZE, RDR_TLS | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? RDR_NO_CSUM : 0))) + if (!rdr.read(op->req.buf, OSD_PACKET_SIZE, RDR_GCM | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? RDR_NO_CSUM : 0))) return false; if (hdr) { @@ -999,7 +990,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr) if (op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) { - if (!rdr.read((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, RDR_GCM)) return false; if (!rdr.read((uint8_t*)op->buf, op->req.sec_rw.len, 0)) return false; @@ -1007,12 +998,12 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr) else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE || op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK) { - if (!rdr.read((uint8_t*)op->buf, op->req.sec_stab.len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, op->req.sec_stab.len, RDR_GCM)) return false; } else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP) { - if (!rdr.read((uint8_t*)op->buf, op->req.sec_read_bmp.len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, op->req.sec_read_bmp.len, RDR_GCM)) return false; } else if (op->req.hdr.opcode == OSD_OP_WRITE) @@ -1022,20 +1013,20 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr) } else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG) { - if (!rdr.read((uint8_t*)op->buf, op->req.show_conf.json_len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, op->req.show_conf.json_len, RDR_GCM)) return false; } } else { - if (!rdr.read(op->reply.buf, OSD_PACKET_SIZE, RDR_TLS | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? RDR_NO_CSUM : 0))) + if (!rdr.read(op->reply.buf, OSD_PACKET_SIZE, RDR_GCM | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? RDR_NO_CSUM : 0))) return false; switched_type: if (op->reply.hdr.opcode == OSD_OP_SEC_READ) { if (op->reply.sec_rw.attr_len > 0) { - if (!rdr.read((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len, RDR_GCM)) return false; } if (op->reply.hdr.retval > 0) @@ -1049,7 +1040,7 @@ switched_type: { if (op->reply.rw.bitmap_len > 0) { - if (!rdr.read((uint8_t*)op->bitmap, op->reply.rw.bitmap_len, RDR_TLS)) + if (!rdr.read((uint8_t*)op->bitmap, op->reply.rw.bitmap_len, RDR_GCM)) return false; } if (op->reply.hdr.retval > 0) @@ -1061,25 +1052,25 @@ switched_type: } else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0) { - if (!rdr.read((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval, RDR_GCM)) return false; } else if ((op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP || op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG) && op->reply.hdr.retval > 0) { - if (!rdr.read((uint8_t*)op->buf, op->reply.hdr.retval, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, op->reply.hdr.retval, RDR_GCM)) return false; } else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0) { - if (!rdr.read((uint8_t*)op->buf, op->reply.describe.result_bytes, RDR_TLS)) + if (!rdr.read((uint8_t*)op->buf, op->reply.describe.result_bytes, RDR_GCM)) return false; } } if (cl->proto_csum_status == MSGR_CSUM_FULL || cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD) { - if (!rdr.read((uint8_t*)&op->csum, 8, RDR_TLS|RDR_NO_CSUM)) + if (!rdr.read((uint8_t*)&op->csum, 8, RDR_GCM|RDR_NO_CSUM)) return false; } if (!rdr.finish()) diff --git a/src/client/msgr_send.cpp b/src/client/msgr_send.cpp index aad3a1fe..ce9f4d6f 100644 --- a/src/client/msgr_send.cpp +++ b/src/client/msgr_send.cpp @@ -13,7 +13,7 @@ #include #include -#define WR_TLS 1 +#define WR_GCM 1 #define WR_XTS 2 #define WR_NO_CSUM 4 @@ -36,6 +36,8 @@ protected: size_t done; public: + constexpr static bool is_ssl = false; + copy_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t *curbuf, size_t bufsize): msgr(msgr), cl(cl), from(cl->write_op_pos), curbuf(curbuf), bufsize(bufsize), done(0) {} @@ -96,6 +98,8 @@ class ssl_op_writer_t: public msgr_op_writer_t size_t done; public: + constexpr static bool is_ssl = true; + ssl_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t *curbuf, size_t bufsize): msgr(msgr), cl(cl), from(cl->write_op_pos), curbuf(curbuf), bufsize(bufsize), done(0) { @@ -106,14 +110,15 @@ public: from = cl->write_op_pos; } - void flush_ssl() + bool flush_ssl() { if (!cl->ssl_handshake_done) { - if (!msgr->ssl_do_handshake(cl)) - return; + if (!msgr->do_tls_handshake(cl)) + return false; + return _flush_ssl(); } - _flush_ssl(); + return true; } bool _flush_ssl() @@ -132,6 +137,8 @@ public: cl->ssl_more_to_buffer = true; return false; } + else + cl->ssl_more_to_buffer = false; } return true; } @@ -175,7 +182,7 @@ public: from -= src_len; return true; } - if (!(flags & WR_TLS) || !cl->ssl_cli) + if (!(flags & WR_GCM) || !cl->ssl_cli) { if (flags & WR_XTS) { @@ -198,7 +205,7 @@ public: { if (!cl->ssl_handshake_done) { - if (!msgr->ssl_do_handshake(cl)) + if (!flush_ssl()) return false; } if (cl->ssl_handshake_done) @@ -237,6 +244,8 @@ class gcm_op_writer_t: public msgr_op_writer_t size_t done; public: + constexpr static bool is_ssl = false; + gcm_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t *curbuf, size_t bufsize): msgr(msgr), cl(cl), from(cl->write_op_pos), curbuf(curbuf), bufsize(bufsize), done(0) { @@ -315,7 +324,7 @@ public: from -= src_len; return true; } - if (!(flags & WR_TLS)) + if (!(flags & WR_GCM)) { if (flags & WR_XTS) { @@ -426,12 +435,12 @@ public: } }; -// FIXME Split into 3 classes - basic, tls and gcm class get_op_writer_t: public msgr_op_writer_t { osd_messenger_t* msgr; osd_client_t* cl; size_t from; + size_t done; size_t enc_size; size_t done_enc; @@ -469,6 +478,7 @@ class get_op_writer_t: public msgr_op_writer_t } } cl->send_list.push_back((iovec){ .iov_base = cl->ssl_out_buf+cl->ssl_out_buf_size, .iov_len = n }); + done += n; cl->ssl_out_buf_size += n; } @@ -482,13 +492,16 @@ class get_op_writer_t: public msgr_op_writer_t if (r > 0) n += r; } while (cl->ssl_out_buf_size+n >= cl->ssl_out_buf_cap); + cl->ssl_more_to_buffer = false; if (n > 0) send_out_buf(n); } public: - get_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl): - msgr(msgr), cl(cl), from(cl->write_op_pos), enc_size(0), done_enc(0) + constexpr static bool is_ssl = true; + + get_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t*, size_t): + msgr(msgr), cl(cl), from(cl->write_op_pos), done(0), enc_size(0), done_enc(0) { } @@ -503,18 +516,15 @@ public: } } - void flush_ssl() + bool flush_ssl() { - if (!cl->ssl_handshake_done) + if (cl->ssl_cli && !cl->ssl_handshake_done) { - if (!msgr->ssl_do_handshake(cl)) - return; + if (!msgr->do_tls_handshake(cl)) + return false; + copy_ssl(); } - if (cl->send_list.size() >= IOV_MAX) - { - return; - } - copy_ssl(); + return true; } bool write(uint8_t *src, size_t src_len, int flags) override @@ -529,13 +539,13 @@ public: { return false; } - if (flags & WR_TLS) + if (flags & WR_GCM) { if (cl->ssl_cli) { if (!cl->ssl_handshake_done) { - if (!msgr->ssl_do_handshake(cl)) + if (!flush_ssl()) return false; } if (cl->ssl_handshake_done) @@ -594,6 +604,7 @@ public: assert(enc_size > 0); cl->write_op->enc_buf = (uint8_t*)malloc_or_die(enc_size); cl->send_list.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = enc_size }); + done += enc_size; } assert(enc_size > 0); msgr->op_encrypted_copy_buf(cl, cl->write_op->enc_buf, enc_size, src, src_len, from, done_enc); @@ -604,6 +615,7 @@ public: if (cl->write_csum_state && !(flags & WR_NO_CSUM)) XXH3_64bits_update(cl->write_csum_state, src+from, src_len-from); cl->send_list.push_back((iovec){ src+from, src_len-from }); + done += src_len-from; cl->write_op_pos += src_len-from; } from = 0; @@ -630,6 +642,11 @@ public: } return true; } + + size_t get_done() + { + return done; + } }; void osd_messenger_t::outbox_push(osd_op_t *cur_op) @@ -741,30 +758,6 @@ void osd_messenger_t::measure_exec(osd_op_t *cur_op) } } -bool osd_messenger_t::ssl_do_handshake(osd_client_t *cl) -{ - if (cl->ssl_handshake_done) - { - return true; - } - int r = SSL_do_handshake(cl->ssl_cli); - if (r > 0) - { - cl->ssl_handshake_done = true; - } - else - { - r = SSL_get_error(cl->ssl_cli, r); - if (r != 0 && r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) - { - fprintf(stderr, "Client %ju TLS handshake error: %s, stopping client\n", cl->client_id, ERR_error_string(ERR_get_error(), NULL)); - cl->io_error = true; - return false; - } - } - return true; -} - bool osd_messenger_t::try_send(osd_client_t *cl) { if (cl->peer_state == PEER_STOPPED || cl->peer_fd < 0) @@ -776,32 +769,11 @@ bool osd_messenger_t::try_send(osd_client_t *cl) return false; } assert(cl->peer_state != PEER_RDMA); - get_op_writer_t wr(this, cl); - while ((cl->write_op || cl->write_ops.size()) && cl->send_list.size() < IOV_MAX) + copy_ops_to_with(cl, NULL, 0); + if (cl->io_error) { - if (!cl->write_op) - { - next_write_op(cl); - wr.reset(); - } - osd_op_t *op = cl->write_op; - if (!op_write_to(cl, wr)) - { - if (cl->io_error) - { - stop_client(cl->client_id); - return true; - } - break; - } - if (!cl->write_op && op->op_type == OSD_OP_IN) - { - cl->send_free_ops.push_back(op); - } - } - if (!cl->send_list.size() && cl->ssl_cli) - { - wr.flush_ssl(); + stop_client(cl->client_id); + return true; } if (!cl->send_list.size()) { @@ -867,6 +839,10 @@ bool osd_messenger_t::try_send(osd_client_t *cl) size_t osd_messenger_t::copy_ops_to(osd_client_t *cl, uint8_t *dst, size_t dst_len) { + if (cl->ssl_cli) + { + return copy_ops_to_with(cl, dst, dst_len); + } if (cl->gcm_enabled) { return copy_ops_to_with(cl, dst, dst_len); @@ -898,10 +874,13 @@ size_t osd_messenger_t::copy_ops_to_with(osd_client_t *cl, uint8_t *dst, size_t cl->send_free_ops.push_back(op); } } - /*FIXME if (!wr.get_done() && cl->ssl_cli) + if constexpr (T::is_ssl) { - wr.flush_ssl(); - }*/ + if (!wr.get_done()) + { + wr.flush_ssl(); + } + } return wr.get_done(); } @@ -1058,7 +1037,7 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr) osd_op_t *op = cl->write_op; // Header if (!wr.write((op->op_type == OSD_OP_IN ? op->reply.buf : op->req.buf), OSD_PACKET_SIZE, - WR_TLS | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? WR_NO_CSUM : 0))) + WR_GCM | (cl->proto_csum_status == MSGR_CSUM_PAYLOAD ? WR_NO_CSUM : 0))) { return false; } @@ -1067,17 +1046,17 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr) { if (op->req.hdr.opcode == OSD_OP_SEC_READ && op->reply.sec_rw.attr_len > 0) { - if (!wr.write((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len, WR_TLS)) + if (!wr.write((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len, WR_GCM)) return false; } else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP && op->reply.hdr.retval > 0) { - if (!wr.write((uint8_t*)op->buf, (size_t)op->reply.hdr.retval, WR_TLS)) + if (!wr.write((uint8_t*)op->buf, (size_t)op->reply.hdr.retval, WR_GCM)) return false; } else if (op->req.hdr.opcode == OSD_OP_READ && op->reply.rw.bitmap_len > 0) { - if (!wr.write((uint8_t*)op->bitmap, op->reply.rw.bitmap_len, WR_TLS)) + if (!wr.write((uint8_t*)op->bitmap, op->reply.rw.bitmap_len, WR_GCM)) return false; } } @@ -1086,12 +1065,12 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr) if ((op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) && op->req.sec_rw.attr_len > 0) { - if (!wr.write((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, WR_TLS)) + if (!wr.write((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, WR_GCM)) return false; } else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP && op->req.sec_read_bmp.len > 0) { - if (!wr.write((uint8_t*)op->buf, (size_t)op->req.sec_read_bmp.len, WR_TLS)) + if (!wr.write((uint8_t*)op->buf, (size_t)op->req.sec_read_bmp.len, WR_GCM)) return false; } } @@ -1101,7 +1080,7 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr) for (int i = 0; i < cl->write_op->iov.count; i++) { auto & iov = cl->write_op->iov.buf[i]; - if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, WR_TLS)) + if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, WR_GCM)) return false; } } @@ -1118,7 +1097,7 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr) cl->proto_csum_status == MSGR_CSUM_PAYLOAD && cl->write_op_pos > OSD_PACKET_SIZE) { cl->write_op->csum = XXH3_64bits_digest(cl->write_csum_state); - if (!wr.write((uint8_t*)&cl->write_op->csum, 8, WR_TLS|WR_NO_CSUM)) + if (!wr.write((uint8_t*)&cl->write_op->csum, 8, WR_GCM|WR_NO_CSUM)) return false; } if (!wr.finish()) diff --git a/src/util/openssl_util.cpp b/src/util/openssl_util.cpp new file mode 100644 index 00000000..e566d547 --- /dev/null +++ b/src/util/openssl_util.cpp @@ -0,0 +1,114 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details) + +#include "openssl_util.h" +#include "str_util.h" + +#include + +X509 *openssl_load_cert(const std::string & file_or_pem) +{ + std::string pem; + BIO *bio = NULL; + if (file_or_pem.substr(0, 5) != "-----") + { + pem = read_file(file_or_pem); + bio = BIO_new_mem_buf(pem.data(), pem.size()); + } + else + bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size()); + if (!bio) + return NULL; + X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); + BIO_free(bio); + return x509; +} + +EVP_PKEY *openssl_load_key(const std::string & file_or_pem) +{ + std::string pem; + BIO *bio = NULL; + if (file_or_pem.substr(0, 5) != "-----") + { + pem = read_file(file_or_pem); + bio = BIO_new_mem_buf(pem.data(), pem.size()); + } + else + bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size()); + if (!bio) + return NULL; + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem) +{ + X509 *cert = openssl_load_cert(file_or_pem); + bool ok = !!cert; + if (cert) + { + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + X509_STORE_add_cert(store, cert); + X509_free(cert); + } + return ok; +} + +bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem) +{ + if (file_or_pem.substr(0, 5) == "-----") + { + return openssl_ctx_add_ca(ssl_ctx, file_or_pem); + } + return file_or_pem.empty() + ? !!SSL_CTX_set_default_verify_paths(ssl_ctx) + : !!SSL_CTX_load_verify_locations(ssl_ctx, file_or_pem.c_str(), NULL); +} + +std::string openssl_get_cn(X509 *x509) +{ + X509_NAME* subj = X509_get_subject_name(x509); + int pos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); + if (pos != -1) + { + X509_NAME_ENTRY* cn = X509_NAME_get_entry(subj, pos); + ASN1_STRING* str = X509_NAME_ENTRY_get_data(cn); + return std::string((const char*)ASN1_STRING_get0_data(str), ASN1_STRING_length(str)); + } + return ""; +} + +bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name) +{ + X509 *cert = openssl_load_cert(file_or_pem); + bool ok = false; + if (cert) + { + common_name = openssl_get_cn(cert); + ok = SSL_CTX_use_certificate(ssl_ctx, cert); + X509_free(cert); + } + return ok; +} + +bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem) +{ + EVP_PKEY *pkey = openssl_load_key(file_or_pem); + bool ok = false; + if (pkey) + { + ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey); + EVP_PKEY_free(pkey); + } + return ok; +} + +bool openssl_bio_nonempty(BIO *bio) +{ + // SSL_ERROR_WANT_WRITE is absolutely non-informative with memory BIO, it basically never happens + // So we have to check memory BIO for outstanding data + char *bio_buf = NULL; + size_t bio_sz = BIO_get_mem_data(bio, &bio_buf); + return bio_sz > 0; +} diff --git a/src/util/openssl_util.h b/src/util/openssl_util.h new file mode 100644 index 00000000..93f055c5 --- /dev/null +++ b/src/util/openssl_util.h @@ -0,0 +1,19 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details) + +#pragma once + +#include + +#ifdef WITH_OPENSSL +#include +#endif + +X509 *openssl_load_cert(const std::string & file_or_pem); +EVP_PKEY *openssl_load_key(const std::string & file_or_pem); +bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem); +bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem); +std::string openssl_get_cn(X509 *x509); +bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name); +bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem); +bool openssl_bio_nonempty(BIO *bio); diff --git a/src/util/xxhash.h b/src/util/xxhash.h index 66266a40..e3838e21 100644 --- a/src/util/xxhash.h +++ b/src/util/xxhash.h @@ -34,6 +34,8 @@ extern "C" { # define XXH_NOESCAPE #endif +#define XXH_SECRET_DEFAULT_SIZE 192 + typedef enum { XXH_OK = 0, XXH_ERROR