Compare commits

..
Author SHA1 Message Date
Vitaliy Filippov 039c524f0a Implement handshake for AES-256-GCM by capturing TLS secrets (pretty shitty approach actually...) 2026-04-26 11:26:34 +03:00
Vitaliy Filippov 4383135434 Extract openssl-related code, wire ssl implementation back 2026-04-26 11:25:38 +03:00
Vitaliy Filippov 767a61e47c Coalesce entries in send_out_buf 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 5d42881736 Support isa-l_crypto for AES-XTS too 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 9d2f6046c6 Fix xts+rdma encrypt errors 2026-04-22 01:41:29 +03:00
Vitaliy Filippov bed4dc11b5 Fix "use-after-realloc" warning 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 4fb7687561 Support isa-l_crypto for AES-GCM 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 1f64e94f2e Remove WITH_OPENSSL from all files except http_client, always require OpenSSL 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 2435209071 Use pools for GCM contexts 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 0eba3e5862 Try to run tests with AES-GCM 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 1ffea58519 Implement direct AES-256-GCM with a static key for benchmark 2026-04-22 01:41:29 +03:00
Vitaliy Filippov cb9ccb8a54 Make sure to send all TLS data before continuing 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 184920102c Parse standard TLS record headers 2026-04-22 01:41:29 +03:00
Vitaliy Filippov d6f1a28096 Omit msgr_tls_record_hdr_t for non-tls data 2026-04-22 01:41:29 +03:00
Vitaliy Filippov cb392ffdfd Allow 2 and 4 byte per block chain_info (allow more than 255 snapshots with encryption) 2026-04-22 01:41:29 +03:00
Vitaliy Filippov a21e4f9503 Implement OSD TLS support 2026-04-22 01:41:29 +03:00
Vitaliy Filippov a9c12d9858 Allow to skip checksums for headers 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 6d40fd86e2 Implement protocol-level checksums (xxhash3) 2026-04-22 01:41:29 +03:00
Vitaliy Filippov a4f47cc67a Include xxhash3 x86dispatch 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 32ab12a880 Do not use scrap_buffer in the client (it would block protocol checksum support) 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 7a40c7f2f6 Support TLS CN authentication and per-image permissions in vitastor-cli serve 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 0f001b0e76 Add VitastorAuthFilter 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 3551525f5e Implement vitastor-cli ls-user, modify-user, remove-user commands 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 6cc899db84 Add image owner/owner_group/reader_group support (for antietcd VitastorAuthFilter) 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 64380856c2 Add security parameter documentation 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 2d9167dd57 Support inline (string PEM) certificates and pkeys 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 04f624f2be Show encryption keys (only IDs) in the listing 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 8b6d978390 Support storing image encryption keys in Vault 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 08401808e8 Prefer local etcd addresses and correctly cycle over them even when they need resolving
Seems slightly overcomplicated...
2026-04-22 01:41:29 +03:00
Vitaliy Filippov 800744b5c3 Support DNS resolving via libc-ares 2026-04-22 01:41:29 +03:00
Vitaliy Filippov a1ac51da24 Batch handle_immediate_ops more 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 64dd98a4d6 Add vitastor-cli create & modify --enc-key parameter 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 3ccfa25b47 Support reading from snapshots encrypted with different keys 2026-04-22 01:41:29 +03:00
Vitaliy Filippov 798ae7c393 Support decryption with multiple keys 2026-04-22 01:41:29 +03:00
Vitaliy Filippov c88bf12484 Allow to return chain_info in response to reads 2026-04-22 01:41:29 +03:00
Vitaliy Filippov e64d8c8c61 Add basic AES-XTS client-side encryption support 2026-04-22 01:41:28 +03:00
Vitaliy Filippov ced3cc3de9 Rework msgr send/receive to allow encryption support 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 43c00538b3 Move fromhexstr() to str_util 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 4cb718de44 Add openapi description 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 45da16995e Slightly fix API return and input types 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 54feea5234 Implement vitastor-cli serve command to serve simple HTTP API 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 157191b770 Implement HTTP server support O_o 2026-04-22 01:41:10 +03:00
Vitaliy Filippov c971a32226 Rename http_response_t to http_message_t 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 14dabf4de5 Extract common HTTP context 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 5776837c58 Support xxhash 32-bit checksums (data_csum_type=xxh3_32) 2026-04-22 01:41:10 +03:00
Vitaliy Filippov e21a10940f Detect block checksums using csum_block_size, not data_csum_type 2026-04-22 01:41:10 +03:00
Vitaliy Filippov ef3c9a0eb4 Add client certificate support 2026-04-22 01:41:10 +03:00
Vitaliy Filippov 55e86dbb29 Do not re-initialize TLS context every connection 2026-04-22 01:41:10 +03:00
Vitaliy Filippov cd46bee266 Add https support to antietcd 2026-04-22 01:41:10 +03:00
Vitaliy Filippov a1130598c0 Implement etcd SSL support via OpenSSL
Maybe I should remove all of this and use libwebsockets :)
2026-04-22 01:41:10 +03:00
Vitaliy Filippov 008ed5b269 Rollback 25ecca7625 - there was no actual use-after-free :) 2026-04-22 01:35:38 +03:00
Vitaliy Filippov 4fffe0f032 Fix vitastor-nfs trace option 2026-04-20 21:51:47 +03:00
Vitaliy Filippov a76d5ccc0d Wakeup callers in rm-data 2026-04-17 13:53:32 +03:00
Vitaliy Filippov 8ed1e180e0 Clear inode_stats in mon 2026-04-14 02:44:46 +03:00
Vitaliy Filippov 8832fc3b14 Fix print to stdout in client 2026-04-14 02:43:50 +03:00
Vitaliy Filippov 0134934c99 Do not skip garbage entries on start (rollback change from 3.0.4) 2026-04-11 12:05:02 +03:00
Vitaliy Filippov 2e36f292bd Fix buffer alignment during metadata clearing on init 2026-04-10 21:56:12 +03:00
Vitaliy Filippov bcc6419760 Auto-select the only RDMA device/port if there is only one 2026-04-08 15:46:10 +03:00
46 changed files with 2351 additions and 1264 deletions
+6 -8
View File
@@ -12,20 +12,18 @@ ARG REL=
WORKDIR /root
RUN set -e -x; \
if [ "$REL" = "buster" ]; then \
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
fi; \
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/*.sources || true; \
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
RUN apt-get update && \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev libisal-crypto-dev \
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev curl nodejs npm node-nan node-bindings && \
apt-get -y build-dep fio && \
apt-get --download-only source fio
+4 -4
View File
@@ -25,7 +25,7 @@ Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't su
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
- [max_cipher_pool_size](#max_cipher_pool_size)
## etcd_client_cert
@@ -141,10 +141,10 @@ Time (in seconds) to wait before retrying after receiving an error from Vault.
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
in case of system clock drift.
## max_aes_xts_pool_size
## max_cipher_pool_size
- Type: integer
- Default: 256
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
+4 -4
View File
@@ -27,7 +27,7 @@ OSD, мониторами и клиентами.
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
- [max_cipher_pool_size](#max_cipher_pool_size)
## etcd_client_cert
@@ -145,10 +145,10 @@ OSD, клиенты и мониторы должны иметь разные п
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
## max_aes_xts_pool_size
## max_cipher_pool_size
- Тип: целое число
- Значение по умолчанию: 256
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
+5 -5
View File
@@ -120,12 +120,12 @@
info_ru: |
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
- name: max_aes_xts_pool_size
- name: max_cipher_pool_size
type: int
default: 256
info: |
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
info_ru: |
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
+1
View File
@@ -262,3 +262,4 @@ Options:
| `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |
+1
View File
@@ -274,3 +274,4 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+1
View File
@@ -37,6 +37,7 @@ function derive_osd_stats(st, prev, prev_diff)
const n = c.count - BigInt(pr && pr.count||0);
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
}
diff.inode_stats = {};
for (const pool_id in st.inode_stats||{})
{
diff.inode_stats[pool_id] = {};
+5 -3
View File
@@ -69,15 +69,17 @@ pkg_check_modules(ISAL libisal)
if (ISAL_LIBRARIES)
add_definitions(-DWITH_ISAL)
endif (ISAL_LIBRARIES)
pkg_check_modules(ISAL_CRYPTO libisal_crypto)
if (ISAL_CRYPTO_LIBRARIES)
add_definitions(-DWITH_ISAL_CRYPTO)
endif (ISAL_CRYPTO_LIBRARIES)
pkg_check_modules(RDMACM librdmacm)
if (RDMACM_LIBRARIES)
add_definitions(-DWITH_RDMACM)
endif (RDMACM_LIBRARIES)
find_package(OpenSSL REQUIRED)
if (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
endif (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
pkg_check_modules(CARES REQUIRED libcares)
include_directories(${CARES_INCLUDE_DIRS})
+6 -13
View File
@@ -344,19 +344,12 @@ corrupted_block:
block_num, block_offset, wr->size, sizeof(heap_entry_t));
goto corrupted_block;
}
if (wr->is_garbage())
{
// Garbage collection is only performed when writing new entries into the block
// because it needs a fake LSN and modified blocks require consecutive modified LSNs
// That's why garbage entries may persist on disk
if (log_level > 5)
{
fprintf(stderr, "Notice: skipping garbage entry %jx:%jx v%ju l%ju in metadata block %u at %u\n",
wr->inode, wr->stripe, wr->version, wr->lsn, block_num, block_offset);
}
block_offset += wr->size;
continue;
}
// Garbage collection is only performed when writing new entries into the block
// because it needs a fake LSN and modified blocks require consecutive modified LSNs
// At the same time, further modifications _after_ putting new entries into the block,
// but _before_ writing it, may mark some entries in it as garbage. That's why garbage
// entries may still be present on disk.
wr->entry_type &= ~BS_HEAP_GARBAGE;
if ((wr->entry_type & BS_HEAP_TYPE) < BS_HEAP_BIG_WRITE ||
(wr->entry_type & BS_HEAP_TYPE) > BS_HEAP_ROLLBACK ||
(wr->entry_type & ~(BS_HEAP_TYPE|BS_HEAP_STABLE)) ||
+1 -1
View File
@@ -311,7 +311,7 @@ resume_8:
uint32_t block_num = recheck_mod[i];
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data)
+5 -4
View File
@@ -12,11 +12,11 @@ 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 ../util/addr_util.cpp ../util/xxh_x86dispatch.c
../util/epoll_manager.cpp etcd_state_client.cpp messenger.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}
)
target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES} ${CARES_LIBRARIES})
target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES} ${CARES_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
target_compile_options(vitastor_common PUBLIC -fPIC)
# libvitastor_client.so
@@ -35,6 +35,7 @@ target_link_libraries(vitastor_client
${IBVERBS_LIBRARIES}
${RDMACM_LIBRARIES}
${OPENSSL_LIBRARIES}
${ISAL_CRYPTO_LIBRARIES}
)
set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
configure_file(vitastor.pc.in vitastor.pc @ONLY)
@@ -101,9 +102,9 @@ 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 ${OPENSSL_LIBRARIES})
target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
add_dependencies(build_tests test_cluster_client)
+7 -4
View File
@@ -590,7 +590,7 @@ void cluster_client_t::on_change_pool_config_hook()
{
if (log_level > 2 && pg_counts[pool_item.first])
{
printf("Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
fprintf(stderr, "Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
pg_counts[pool_item.first], pool_item.second.real_pg_count);
}
// At this point, all pool operations should have been suspended
@@ -1294,8 +1294,8 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
unsigned bitmap_mem = object_bitmap_size +
op->parts.size() * pg_data_size *
(pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
// read chain info - 1 byte per block
+ (op->enc ? op->len/pool_cfg.bitmap_granularity : 0));
// read chain_info - max 4 bytes per block
+ (op->enc ? osd_op_rw_t::chain_info_bytes(op->enc->chain_size)*op->len/pool_cfg.bitmap_granularity : 0));
if (!op->bitmap_buf || op->bitmap_buf_size < bitmap_mem)
{
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
@@ -1452,7 +1452,10 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
op->inflight_count++;
uint32_t pg_data_size = (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
uint64_t pg_bitmap_size = pg_data_size * (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
+ (op->opcode == OSD_OP_READ && op->enc ? pool_cfg.data_block_size/pool_cfg.bitmap_granularity : 0));
// read chain_info - max 4 bytes per block
+ (op->opcode == OSD_OP_READ && op->enc
? osd_op_rw_t::chain_info_bytes(op->enc->chain_size)*pool_cfg.data_block_size/pool_cfg.bitmap_granularity
: 0));
uint64_t meta_rev = 0;
if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_DELETE && !op->deoptimise_snapshot)
{
+1 -1
View File
@@ -84,7 +84,7 @@ class writeback_cache_t;
struct inode_cache_t
{
std::vector<inode_t> chain;
std::vector<inode_t> chain; // only parents from the same pool
uint8_t *key_data = NULL;
osd_op_enc_t *op_enc = NULL;
bool readonly = false;
+1 -98
View File
@@ -19,6 +19,7 @@
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "openssl_util.h"
#endif
// libc-ares
@@ -163,104 +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)
{
BIO *bio = NULL;
if (file_or_pem.substr(0, 5) != "-----")
{
std::string 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)
{
-12
View File
@@ -8,10 +8,6 @@
#include <functional>
#include "json11/json11.hpp"
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#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
+11 -85
View File
@@ -10,17 +10,11 @@
#include <stdexcept>
#include "addr_util.h"
#include "str_util.h"
#include "messenger.h"
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
#include "http_client.h"
#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#endif
#include <sys/poll.h>
@@ -124,43 +118,7 @@ void msgr_iothread_t::run()
void osd_messenger_t::init()
{
if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty())
{
// Initialize TLS context
// FIXME: require OpenSSL
#ifndef WITH_OPENSSL
fprintf(stderr, "Vitastor is built without OpenSSL support\n");
exit(1);
#else
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());
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;
}
}
#endif
}
init_tls();
#ifdef WITH_RDMACM
if (use_rdmacm)
{
@@ -339,21 +297,15 @@ osd_messenger_t::~osd_messenger_t()
rdmacm_evch = NULL;
}
#endif
for (auto encrypt_ctx: encrypt_ctx_pool)
for (auto encrypt_ctx: encrypt_xts_pool)
{
destroy_aes_xts_encrypt(encrypt_ctx);
}
for (auto decrypt_ctx: decrypt_ctx_pool)
for (auto decrypt_ctx: decrypt_xts_pool)
{
destroy_aes_xts_decrypt(decrypt_ctx);
}
#ifdef WITH_OPENSSL
if (ssl_ctx)
{
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
}
#endif
destroy_tls();
}
void osd_messenger_t::parse_config(const json11::Json & config)
@@ -388,9 +340,9 @@ void osd_messenger_t::parse_config(const json11::Json & config)
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
this->rdma_max_msg = 129*1024;
#endif
this->max_aes_xts_pool_size = config["max_aes_xts_pool_size"].uint64_value();
if (!this->max_aes_xts_pool_size)
this->max_aes_xts_pool_size = 256;
this->max_cipher_pool_size = config["max_cipher_pool_size"].uint64_value();
if (!this->max_cipher_pool_size)
this->max_cipher_pool_size = 256;
if (config["proto_checksums"].is_null())
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
else if (config["proto_checksums"].is_bool())
@@ -420,7 +372,7 @@ void osd_messenger_t::parse_config(const json11::Json & config)
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
this->receive_buffer_size = 65536;
this->use_sync_send_recv = config["use_sync_send_recv"].bool_value() ||
config["use_sync_send_recv"].uint64_value();
config["use_sync_send_recv"].uint64_value() || !ringloop;
this->min_zerocopy_send_size = config["min_zerocopy_send_size"].is_null()
? DEFAULT_MIN_ZEROCOPY_SEND_SIZE
: (int)config["min_zerocopy_send_size"].int64_value();
@@ -642,10 +594,7 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
handle_peer_epoll(peer_fd, epoll_events);
});
// Check OSD number
if (!tls_cert.empty())
{
ssl_init(cl, false);
}
init_tls_client(cl);
check_peer_config(cl);
}
@@ -887,10 +836,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);
if (!tls_cert.empty())
{
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)
{
@@ -905,26 +851,6 @@ void osd_messenger_t::accept_connections(int listen_fd)
}
}
void osd_messenger_t::ssl_init(osd_client_t *cl, bool server_mode)
{
#ifdef WITH_OPENSSL
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 (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);
#endif
}
#ifdef WITH_RDMA
msgr_rdma_context_t* osd_messenger_t::choose_rdma_context(osd_client_t *cl)
{
+75 -29
View File
@@ -12,8 +12,10 @@
#include <deque>
#include <vector>
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/aes_gcm.h>
#endif
#include "../util/xxh_x86dispatch.h"
@@ -44,6 +46,9 @@
#define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024
#define AES_256_GCM_KEY_SIZE 32
#define AES_256_GCM_IV_SIZE 12
struct msgr_sendp_t
{
osd_op_t *op;
@@ -60,10 +65,12 @@ struct op_aes_xts_decrypt_t;
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
// Standard TLS record header. We are only interested in the record size
struct __attribute__((__packed__)) msgr_tls_record_hdr_t
{
uint8_t encrypted;
uint32_t size;
uint8_t content_type;
uint16_t version;
uint16_t size;
};
struct osd_client_t
@@ -88,18 +95,36 @@ struct osd_client_t
msgr_rdma_connection_t *rdma_conn = NULL;
#endif
#ifdef WITH_OPENSSL
SSL *ssl_cli = NULL;
BIO *write_to_ssl = NULL;
// FIXME: use custom bio to avoid 1 more memory copy?
BIO *read_from_ssl = NULL;
uint8_t *ssl_out_buf = NULL;
size_t ssl_out_buf_size = 0, ssl_out_buf_cap = 0;
bool ssl_handshake_done = false;
bool ssl_want_write = false;
int ssl_handshake_pending = 0;
msgr_tls_record_hdr_t ssl_read_record;
size_t ssl_read_header_size = 0;
bool ssl_more_to_buffer = false;
bool gcm_enabled = false;
std::vector<uint8_t> my_secret, peer_secret;
std::vector<uint8_t> my_key, peer_key;
uint64_t my_iv_ctr, peer_iv_ctr;
#ifdef WITH_ISAL_CRYPTO
isal_gcm_key_data my_key_isal, peer_key_isal;
isal_gcm_context_data *enc_ctx = NULL;
isal_gcm_context_data *dec_ctx = NULL;
#else
EVP_CIPHER_CTX *enc_ctx = NULL;
EVP_CIPHER_CTX *dec_ctx = NULL;
#endif
uint8_t enc_tag[16];
size_t enc_tag_size = 0;
uint8_t dec_tag[16];
size_t dec_tag_size = 0;
// Read state
bool io_error = false;
int read_ready = 0;
osd_op_t *read_op = NULL;
size_t read_op_size = 0;
@@ -107,11 +132,11 @@ struct osd_client_t
iovec read_iov = { 0 };
msghdr read_msg = { 0 };
std::vector<iovec> recv_list;
size_t recv_list_size = 0;
std::vector<int> recv_flags;
uint64_t read_op_id = 1;
bool check_sequencing = false;
bool enable_pg_locks = false;
op_aes_xts_decrypt_t *decrypt_ctx = NULL;
op_aes_xts_decrypt_t *xts_dec_ctx = NULL;
size_t read_op_inline_decrypt_pos = 0;
size_t read_op_inline_decrypt_in = 0;
int proto_csum_status = 0;
@@ -137,7 +162,7 @@ struct osd_client_t
size_t send_list_size = 0;
std::deque<osd_op_t*> send_free_ops;
std::vector<osd_op_t*> zc_free_list;
op_aes_xts_encrypt_t *encrypt_ctx = NULL;
op_aes_xts_encrypt_t *xts_enc_ctx = NULL;
XXH3_state_t* write_csum_state = NULL;
~osd_client_t();
@@ -216,9 +241,21 @@ struct osd_messenger_t;
struct rdmacm_connecting_t;
#endif
class msgr_op_reader_t;
class msgr_op_writer_t;
struct __attribute__((visibility("default"))) osd_messenger_t
{
protected:
friend class copy_op_reader_t;
friend class ssl_op_reader_t;
friend class gcm_op_reader_t;
friend class get_op_reader_t;
friend class copy_op_writer_t;
friend class ssl_op_writer_t;
friend class gcm_op_writer_t;
friend class get_op_writer_t;
int keepalive_timer_id = -1;
uint32_t receive_buffer_size = 0;
@@ -230,7 +267,7 @@ protected:
bool use_sync_send_recv = false;
int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE;
int iothread_count = 0;
int max_aes_xts_pool_size = 256;
int max_cipher_pool_size = 256;
std::string tls_cert;
std::string tls_key;
@@ -253,18 +290,19 @@ protected:
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
#endif
#ifdef WITH_OPENSSL
SSL_CTX *ssl_ctx = NULL;
EVP_KDF_CTX *kdf_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);
bool ssl_do_encrypt(osd_client_t *cl);
size_t ssl_do_encrypt_to(osd_client_t *cl, uint8_t *buf, size_t size);
bool ssl_op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, bool skip_csum, size_t & from, size_t & done);
size_t ssl_op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void ssl_op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
#endif
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);
bool finalize_tls_handshake(osd_client_t *cl);
bool derive_aes_keys(osd_client_t *cl, bool update_my, bool update_peer);
std::vector<msgr_iothread_t*> iothreads;
std::vector<uint64_t> read_ready_clients;
@@ -272,8 +310,16 @@ protected:
// We don't use ringloop->set_immediate here because we may have no ringloop in client :)
std::deque<osd_op_t*> set_immediate_ops;
std::vector<op_aes_xts_encrypt_t*> encrypt_ctx_pool;
std::vector<op_aes_xts_decrypt_t*> decrypt_ctx_pool;
std::vector<op_aes_xts_encrypt_t*> encrypt_xts_pool;
std::vector<op_aes_xts_decrypt_t*> decrypt_xts_pool;
#ifdef WITH_ISAL_CRYPTO
std::vector<isal_gcm_context_data*> encrypt_gcm_pool;
std::vector<isal_gcm_context_data*> decrypt_gcm_pool;
#else
std::vector<EVP_CIPHER_CTX*> encrypt_gcm_pool;
std::vector<EVP_CIPHER_CTX*> decrypt_gcm_pool;
#endif
public:
timerfd_manager_t *tfd = NULL;
@@ -342,32 +388,32 @@ protected:
bool try_send(osd_client_t *cl);
void handle_send(int result, bool prev, bool more, osd_client_t *cl);
size_t op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
bool op_write_to(osd_client_t *cl, msgr_op_writer_t & wr);
void next_write_op(osd_client_t *cl);
bool op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, bool skip_csum, size_t & from, size_t & done);
bool op_copy_data_to(osd_client_t *cl, uint8_t *dst, size_t dst_len, size_t & from, size_t & done);
size_t copy_ops_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
template<typename T> size_t copy_ops_to_with(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void handle_read(int result, osd_client_t *cl);
bool handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
template<typename T> size_t handle_buffer_with(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
bool handle_hdr(osd_client_t *cl);
bool allocate_op_buffers(osd_client_t *cl);
bool allocate_reply_buffers(osd_client_t *cl, osd_op_t *op);
bool op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done);
void op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst);
void op_alloc_temp_buffers(osd_op_t *op, int i);
bool op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr);
bool handle_finished_op(osd_client_t *cl);
void handle_immediate_ops();
bool op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
bool op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
void op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc);
void op_encrypt_free(osd_client_t* cl);
void op_decrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc);
void op_decrypt_start(osd_client_t* cl);
void op_decrypt_inline(osd_client_t* cl);
void op_decrypt_free(osd_client_t* cl);
#ifdef WITH_RDMA
void try_send_rdma(osd_client_t *cl);
int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len);
bool init_recv_rdma(osd_client_t *cl);
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
+421 -111
View File
@@ -3,13 +3,30 @@
#include <assert.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/isal_crypto_api.h>
#endif
#include <mutex>
#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 <openssl/kdf.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MSGR_HSP_HS 1
#define MSGR_HSP_SEND 2
#define MSGR_HSP_RECV 4
op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
@@ -21,16 +38,13 @@ op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
ERR_print_errors_fp(stderr);
abort();
}
#else
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
abort();
#endif
}
op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
{
assert(!encrypted);
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX_free(ctx);
#endif
if (tmp)
@@ -52,7 +66,7 @@ void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t blo
tmp = NULL;
tmp_size = 0;
}
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) != 1)
{
ERR_print_errors_fp(stderr);
@@ -63,9 +77,12 @@ void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t blo
void op_aes_xts_encrypt_t::encrypt_block(uint8_t *in, uint8_t *out)
{
#ifdef WITH_OPENSSL
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_xts_enc_256(key+32, key, iv, block_size, in, out);
assert(r == 0 || r == ISAL_CRYPTO_ERR_XTS_SAME_KEYS);
#else
if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) != 1)
{
ERR_print_errors_fp(stderr);
@@ -98,7 +115,10 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
done_out += max_out;
tmp_pos += max_out;
if (tmp_pos >= block_size)
{
encrypted = false;
done_in += 1;
}
}
else if (max_in < block_size - offset%block_size)
{
@@ -126,7 +146,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
encrypted = true;
memcpy(out, tmp, max_out);
tmp_pos = max_out;
done_in += max_in;
done_in += max_in-1;
offset += max_in;
done_out += max_out;
}
@@ -158,7 +178,7 @@ void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx)
op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
@@ -170,29 +190,27 @@ op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
ERR_print_errors_fp(stderr);
abort();
}
#else
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
abort();
#endif
}
op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
{
assert(!decrypted);
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX_free(ctx);
#endif
if (tmp)
free(tmp);
}
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size)
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
{
assert(!decrypted);
this->start_offset = start_offset;
this->key_chain = chain_size > 1 ? key_chain : 0;
this->chain_size = chain_size > 1 ? chain_size : 0;
this->key_indexes = chain_size > 1 ? key_indexes : NULL;
this->key_chain = key_chain;
this->chain_size = chain_size;
this->key_indexes = key_indexes;
this->key_index_bytes = osd_op_rw_t::chain_info_bytes(chain_size);
assert(chain_size <= 1 || key_indexes != NULL);
this->block_size = block_size;
this->offset = 0;
@@ -203,7 +221,7 @@ void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t
tmp = NULL;
tmp_size = 0;
}
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (chain_size == 1 && key_chain[0] && EVP_DecryptInit_ex(ctx, NULL, NULL, key_chain[0], NULL) != 1)
{
ERR_print_errors_fp(stderr);
@@ -215,21 +233,35 @@ void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t
void op_aes_xts_decrypt_t::decrypt_block(uint8_t *in, uint8_t *out)
{
uint8_t *key = NULL;
if (chain_size)
if (chain_size > 1)
{
assert(key_indexes[offset/block_size] < chain_size);
key = key_chain[key_indexes[offset/block_size]];
if (!key)
{
if (in != out)
memcpy(out, in, block_size);
return;
}
uint32_t key_index = key_index_bytes == 1
? ((uint8_t*)key_indexes)[offset/block_size]
: (key_index_bytes == 2
? ((uint16_t*)key_indexes)[offset/block_size]
: (key_index_bytes == 4
? ((uint32_t*)key_indexes)[offset/block_size]
: UINT32_MAX));
assert(key_index < chain_size);
key = key_chain[key_index];
}
else
{
key = key_chain[0];
}
if (!key)
{
if (in != out)
memcpy(out, in, block_size);
return;
}
#ifdef WITH_OPENSSL
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1)
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_xts_dec_256(key+32, key, iv, block_size, in, out);
assert(r == 0 || r == ISAL_CRYPTO_ERR_XTS_SAME_KEYS);
#else
if (EVP_DecryptInit_ex(ctx, NULL, NULL, chain_size == 1 ? NULL : key, iv) != 1)
{
ERR_print_errors_fp(stderr);
abort();
@@ -264,7 +296,10 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
done_out += max_out;
tmp_pos += max_out;
if (tmp_pos >= block_size)
{
decrypted = false;
done_in += 1;
}
}
else if (max_in < block_size - offset%block_size)
{
@@ -293,7 +328,7 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
if (out)
memcpy(out, tmp, max_out);
tmp_pos = max_out;
done_in += max_in;
done_in += max_in-1;
offset += max_in;
done_out += max_out;
}
@@ -325,101 +360,65 @@ void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx)
delete decrypt_ctx;
}
bool osd_messenger_t::op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
void osd_messenger_t::op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc)
{
auto op = cl->write_op;
auto & op_pos = cl->write_op_pos;
assert(op->req.hdr.opcode == OSD_OP_WRITE);
if (!from)
if (!cl->xts_enc_ctx)
{
if (!cl->encrypt_ctx)
if (encrypt_xts_pool.size())
{
if (encrypt_ctx_pool.size())
{
cl->encrypt_ctx = encrypt_ctx_pool.back();
encrypt_ctx_pool.pop_back();
}
else
cl->encrypt_ctx = new op_aes_xts_encrypt_t();
cl->xts_enc_ctx = encrypt_xts_pool.back();
encrypt_xts_pool.pop_back();
}
assert(op->enc->key_chain[0]);
cl->encrypt_ctx->start(op->enc->key_chain[0], op->req.rw.offset, op->enc->bitmap_granularity);
}
for (int i = 0; i < op->iov.count; i++)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len || cl->encrypt_ctx->has_buffered())
{
if (done >= enc_len)
return false;
size_t done_in = 0;
size_t done_out = 0;
cl->encrypt_ctx->update(plain+from, plain_len-from, enc_buf+done, enc_len-done, done_in, done_out);
if (cl->write_csum_state && done_out > 0)
XXH3_64bits_update(cl->write_csum_state, enc_buf+done, done_out);
done += done_out;
op_pos += done_in;
from += done_in;
}
from -= plain_len;
}
if (cl->encrypt_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->encrypt_ctx;
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
cl->xts_enc_ctx = new op_aes_xts_encrypt_t();
assert(cl->write_op->enc->key_chain[0]);
cl->xts_enc_ctx->start(cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
}
while (done_plain < plain_len && done_enc < enc_len)
{
size_t done_in = 0;
size_t done_out = 0;
cl->xts_enc_ctx->update(plain+done_plain, plain_len-done_plain, enc_buf+done_enc, enc_len-done_enc, done_in, done_out);
if (cl->write_csum_state && done_out > 0)
XXH3_64bits_update(cl->write_csum_state, enc_buf+done_enc, done_out);
done_enc += done_out;
cl->write_op_pos += done_in;
done_plain += done_in;
}
return true;
}
bool osd_messenger_t::op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
void osd_messenger_t::op_decrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc)
{
op_decrypt_start(cl);
auto op = cl->read_op;
assert(op->req.hdr.opcode == OSD_OP_READ);
for (int i = 0; i < op->iov.count; i++)
while (done_plain < plain_len && done_enc < enc_len)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len)
{
if (done >= enc_len)
return false;
size_t done_in = 0;
size_t done_out = 0;
// plain == NULL means skip output
cl->decrypt_ctx->update(enc_buf+done, enc_len-done, plain ? plain+from : NULL, plain_len-from, done_in, done_out);
if (cl->read_csum_state && done_in > 0)
XXH3_64bits_update(cl->read_csum_state, enc_buf+done, done_in);
done += done_in;
cl->read_op_pos += done_out;
cl->read_op_inline_decrypt_in += done_in;
from += done_out;
if (!done_out)
return false;
}
from -= plain_len;
size_t done_in = 0;
size_t done_out = 0;
// plain == NULL means skip output
cl->xts_dec_ctx->update(enc_buf+done_enc, enc_len-done_enc, plain ? plain+done_plain : NULL, plain_len-done_plain, done_in, done_out);
if (cl->read_csum_state && done_in > 0)
XXH3_64bits_update(cl->read_csum_state, enc_buf+done_enc, done_in);
done_enc += done_in;
cl->read_op_pos += done_out;
cl->read_op_inline_decrypt_in += done_in;
done_plain += done_out;
}
op_decrypt_free(cl);
return true;
}
void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
{
if (!cl->decrypt_ctx)
if (!cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size())
if (decrypt_xts_pool.size())
{
cl->decrypt_ctx = decrypt_ctx_pool.back();
decrypt_ctx_pool.pop_back();
cl->xts_dec_ctx = decrypt_xts_pool.back();
decrypt_xts_pool.pop_back();
}
else
cl->decrypt_ctx = new op_aes_xts_decrypt_t();
cl->xts_dec_ctx = new op_aes_xts_decrypt_t();
auto & enc = cl->read_op->enc;
cl->decrypt_ctx->start(enc->key_chain, enc->chain_size,
assert(cl->read_op->req.hdr.opcode == OSD_OP_READ);
cl->xts_dec_ctx->start(enc->key_chain, enc->chain_size,
(cl->read_op->req.rw.flags & OSD_OP_RETURN_CHAIN) ? (uint8_t*)cl->read_op->bitmap + enc->read_chain_bitmap_pos : 0,
cl->read_op->req.rw.offset, enc->bitmap_granularity);
}
@@ -451,7 +450,7 @@ void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
size_t out_len = op->iov.buf[j].iov_len - from_out;
size_t done_in = 0;
size_t done_out = 0;
cl->decrypt_ctx->update(in, in_len, out, out_len, done_in, done_out);
cl->xts_dec_ctx->update(in, in_len, out, out_len, done_in, done_out);
if (done_in >= in_len)
{
i++;
@@ -468,17 +467,328 @@ void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
from_out += done_out;
}
assert(j >= op->iov.count);
op_decrypt_free(cl);
}
void osd_messenger_t::op_decrypt_free(osd_client_t* cl)
{
if (cl->decrypt_ctx)
if (cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->decrypt_ctx;
if (decrypt_xts_pool.size() > max_cipher_pool_size)
delete cl->xts_dec_ctx;
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
decrypt_xts_pool.push_back(cl->xts_dec_ctx);
cl->xts_dec_ctx = NULL;
}
}
void osd_messenger_t::op_encrypt_free(osd_client_t* cl)
{
if (cl->xts_enc_ctx)
{
if (encrypt_xts_pool.size() > max_cipher_pool_size)
delete cl->xts_enc_ctx;
else
encrypt_xts_pool.push_back(cl->xts_enc_ctx);
cl->xts_enc_ctx = NULL;
}
}
struct tls_secrets_t
{
std::vector<uint8_t> client_secret;
std::vector<uint8_t> server_secret;
};
// Sadly we have to use a global variable to capture TLS 1.3 secrets
static std::mutex logged_secrets_mu;
static std::map<const SSL*, tls_secrets_t> logged_secrets;
static void openssl_key_log(const SSL *ssl, const char *line)
{
// Format: <CLIENT|SERVER>_TRAFFIC_SECRET_0 <server_random> <secret>
bool is_client_secret = !strncmp(line, "CLIENT_TRAFFIC_SECRET_0 ", strlen("CLIENT_TRAFFIC_SECRET_0 "));
bool is_server_secret = !strncmp(line, "SERVER_TRAFFIC_SECRET_0 ", strlen("SERVER_TRAFFIC_SECRET_0 "));
if (!is_client_secret && !is_server_secret)
return;
const char *hex = strchr(line+strlen("CLIENT_TRAFFIC_SECRET_0 "), ' ');
if (!hex)
return;
hex++;
size_t len = strlen(hex);
logged_secrets_mu.lock();
auto & secrets = logged_secrets[ssl];
logged_secrets_mu.unlock();
auto & secret = is_client_secret ? secrets.client_secret : secrets.server_secret;
secret.resize(len/2);
fromhexstr(hex, len, secret.data(), secret.size());
}
static bool derive_kdf(EVP_KDF_CTX* kdf_ctx, const uint8_t* insecret, size_t insecret_len,
const uint8_t* salt, size_t salt_len, const char *label, uint8_t *key, size_t size)
{
OSSL_PARAM params[5];
int n = 0;
params[n++] = OSSL_PARAM_construct_utf8_string("digest", (char*)"sha384", (size_t)7);
params[n++] = OSSL_PARAM_construct_octet_string("key", (void*)insecret, insecret_len);
params[n++] = OSSL_PARAM_construct_octet_string("info", (void*)label, strlen(label)+1);
if (salt)
params[n++] = OSSL_PARAM_construct_octet_string("salt", (void*)salt, salt_len);
params[n++] = OSSL_PARAM_construct_end();
assert(n <= sizeof(params)/sizeof(OSSL_PARAM));
if (EVP_KDF_CTX_set_params(kdf_ctx, params) <= 0)
{
ERR_print_errors_fp(stderr);
return false;
}
if (EVP_KDF_derive(kdf_ctx, key, size, NULL) <= 0)
{
ERR_print_errors_fp(stderr);
return false;
}
return true;
}
bool osd_messenger_t::derive_aes_keys(osd_client_t *cl, bool update_my, bool update_peer)
{
std::vector<uint8_t> old_my = cl->my_key, old_peer = cl->peer_key;
if (!cl->my_secret.size() || !cl->peer_secret.size())
{
assert(cl->ssl_cli);
logged_secrets_mu.lock();
auto & secrets = logged_secrets[cl->ssl_cli];
cl->my_secret = std::move(cl->is_incoming ? secrets.client_secret : secrets.server_secret);
cl->peer_secret = std::move(!cl->is_incoming ? secrets.client_secret : secrets.server_secret);
logged_secrets.erase(cl->ssl_cli);
logged_secrets_mu.unlock();
SSL_free(cl->ssl_cli);
cl->ssl_cli = NULL;
cl->gcm_enabled = true;
cl->write_to_ssl = NULL;
cl->read_from_ssl = NULL;
if (cl->my_secret.size() < 32 || cl->peer_secret.size() < 32)
{
fprintf(stderr, "Client %ju error: failed to capture TLS handshake results\n", cl->client_id);
return false;
}
}
// Both keys include AES key and iv + xxhash3 secret
const auto len = AES_256_GCM_KEY_SIZE + AES_256_GCM_IV_SIZE + XXH_SECRET_DEFAULT_SIZE;
cl->my_key.resize(len);
cl->peer_key.resize(len);
bool ok = true;
if (update_my || !old_my.size())
{
ok = ok && derive_kdf(kdf_ctx, cl->my_secret.data(), cl->my_secret.size(),
old_my.size() ? old_my.data() : NULL, old_my.size(),
cl->is_incoming ? "server key" : "client key",
cl->my_key.data(), len);
#ifdef WITH_ISAL_CRYPTO
if (ok)
isal_aes_gcm_pre_256(cl->my_key.data(), &cl->my_key_isal);
#endif
cl->my_iv_ctr = 0;
}
if (update_peer || !old_peer.size())
{
ok = ok && derive_kdf(kdf_ctx, cl->peer_secret.data(), cl->peer_secret.size(),
old_peer.size() ? old_peer.data() : NULL, old_peer.size(),
!cl->is_incoming ? "server key" : "client key",
cl->peer_key.data(), len);
#ifdef WITH_ISAL_CRYPTO
if (ok)
isal_aes_gcm_pre_256(cl->peer_key.data(), &cl->peer_key_isal);
#endif
cl->peer_iv_ctr = 0;
}
return ok;
}
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_keylog_callback(ssl_ctx, openssl_key_log);
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);
EVP_KDF *kdf;
ok = ok && (kdf = EVP_KDF_fetch(NULL, "hkdf", NULL));
ok = ok && (kdf_ctx = EVP_KDF_CTX_new(kdf));
if (kdf)
EVP_KDF_free(kdf);
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);
cl->ssl_handshake_pending = MSGR_HSP_HS;
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_pending & MSGR_HSP_HS))
return true;
int r = SSL_do_handshake(cl->ssl_cli);
if (r > 0)
{
// Server-side OpenSSL treats handshake as finalized only when receiving
// the first message, so we transmit 1 byte after connecting and only then
// finalize the handshake
cl->ssl_handshake_pending = MSGR_HSP_SEND|MSGR_HSP_RECV;
if (cl->write_state == 0 && from_recv)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
}
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;
}
bool osd_messenger_t::finalize_tls_handshake(osd_client_t *cl)
{
if (cl->ssl_handshake_pending)
return true;
// Capture secrets and switch to direct AES-256-GCM encryption
if (!derive_aes_keys(cl, true, true))
{
cl->io_error = true;
return false;
}
if (cl->read_op)
{
assert(!cl->read_op_pos);
delete cl->read_op;
cl->read_op = NULL;
}
if (cl->write_op)
{
assert(!cl->write_op_pos);
cl->write_ops.insert(cl->write_ops.begin(), cl->write_op);
cl->write_op = NULL;
}
if (cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
// Switched to direct AES-GCM, stop SSL callers
return false;
}
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;
}
if (kdf_ctx)
{
EVP_KDF_CTX_free(kdf_ctx);
kdf_ctx = NULL;
}
}
+9 -9
View File
@@ -3,17 +3,18 @@
#include <stdint.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/aes_xts.h>
#endif
#include "../util/xxh_x86dispatch.h"
// WITH_OPENSSL is left to possibly support other crypto libraries
#ifdef WITH_OPENSSL
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#endif
class op_aes_xts_encrypt_t
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
uint64_t start_offset = 0;
@@ -31,7 +32,6 @@ public:
op_aes_xts_encrypt_t();
~op_aes_xts_encrypt_t();
inline bool has_buffered() { return encrypted; };
void start(uint8_t *key, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
@@ -40,13 +40,14 @@ void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
class op_aes_xts_decrypt_t
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
uint64_t start_offset = 0;
uint8_t **key_chain = NULL;
size_t chain_size = 0;
uint8_t *key_indexes = NULL;
void *key_indexes = NULL;
int key_index_bytes = 0;
size_t offset = 0;
size_t block_size = 0;
uint8_t *tmp = NULL;
@@ -60,8 +61,7 @@ public:
op_aes_xts_decrypt_t();
~op_aes_xts_decrypt_t();
inline bool has_buffered() { return decrypted; };
void start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size);
void start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
+1 -4
View File
@@ -8,6 +8,7 @@
osd_op_t::~osd_op_t()
{
assert(!bs_op);
assert(!op_data);
if (bitmap_buf)
{
free(bitmap_buf);
@@ -26,10 +27,6 @@ osd_op_t::~osd_op_t()
{
free(enc_buf);
}
if (op_data)
{
free(op_data);
}
}
bool osd_op_t::is_recovery_related()
+1 -1
View File
@@ -182,7 +182,7 @@ struct __attribute__((visibility("default"))) osd_op_t
void *bitmap = NULL;
unsigned bitmap_len = 0;
unsigned bmp_data = 0;
void *bitmap_buf = NULL;
uint8_t *bitmap_buf = NULL;
void *rmw_buf = NULL;
std::shared_ptr<osd_op_enc_t> enc;
uint8_t *enc_buf = NULL;
+6 -24
View File
@@ -568,29 +568,6 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
cl->rdma_conn->cur_send++;
}
int osd_messenger_t::try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
{
int total_dst_len = dst_len;
while (dst_len > 0 && (cl->write_op || cl->write_ops.size()))
{
next_write_op(cl);
osd_op_t *op = cl->write_op;
size_t copied = op_copy_to(cl, dst, dst_len);
if (!copied)
{
break;
}
dst += copied;
dst_len -= copied;
if (!cl->write_op && op->op_type == OSD_OP_IN)
{
// this is a reply, free the op after sending it
cl->send_free_ops.push_back(op);
}
}
return total_dst_len-dst_len;
}
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
{
auto rc = cl->rdma_conn;
@@ -616,7 +593,12 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
if (dst_len > rc->max_msg)
dst_len = rc->max_msg;
copied = try_send_rdma_copy(cl, dst, dst_len);
copied = copy_ops_to(cl, dst, dst_len);
if (cl->io_error)
{
stop_client(cl->client_id);
return;
}
if (copied > 0)
{
rc->send_out_pos += copied;
+1 -1
View File
@@ -79,7 +79,7 @@ struct msgr_rdma_connection_t
msgr_rdma_buf_t recv_buf;
std::deque<uint64_t> send_sizes;
msgr_rdma_buf_t send_out;
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
size_t send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
bool send_out_full = false;
~msgr_rdma_connection_t();
+4
View File
@@ -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)
{
File diff suppressed because it is too large Load Diff
+830 -475
View File
File diff suppressed because it is too large Load Diff
+32 -15
View File
@@ -9,12 +9,10 @@
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#endif
void osd_client_t::cancel_ops()
{
@@ -86,21 +84,21 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id);
}
}
if (cl->encrypt_ctx)
if (cl->xts_enc_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_encrypt(cl->encrypt_ctx);
if (encrypt_xts_pool.size() > max_cipher_pool_size)
destroy_aes_xts_encrypt(cl->xts_enc_ctx);
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
encrypt_xts_pool.push_back(cl->xts_enc_ctx);
cl->xts_enc_ctx = NULL;
}
if (cl->decrypt_ctx)
if (cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_decrypt(cl->decrypt_ctx);
if (decrypt_xts_pool.size() > max_cipher_pool_size)
destroy_aes_xts_decrypt(cl->xts_dec_ctx);
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
decrypt_xts_pool.push_back(cl->xts_dec_ctx);
cl->xts_dec_ctx = NULL;
}
// First set state to STOPPED so another stop_client() call doesn't try to free it again
cl->refs++;
@@ -223,7 +221,10 @@ osd_client_t::~osd_client_t()
{
if (op)
{
delete op;
if (!((size_t)op & 7))
delete op;
else
free((void*)((size_t)op & ~(size_t)7));
}
}
#ifndef __MOCK__
@@ -245,7 +246,24 @@ osd_client_t::~osd_client_t()
XXH3_freeState(write_csum_state);
write_csum_state = NULL;
}
#ifdef WITH_OPENSSL
if (enc_ctx)
{
#ifdef WITH_ISAL_CRYPTO
free(enc_ctx);
#else
EVP_CIPHER_CTX_free(enc_ctx);
#endif
enc_ctx = NULL;
}
if (dec_ctx)
{
#ifdef WITH_ISAL_CRYPTO
free(dec_ctx);
#else
EVP_CIPHER_CTX_free(dec_ctx);
#endif
dec_ctx = NULL;
}
if (ssl_cli)
{
SSL_free(ssl_cli);
@@ -258,5 +276,4 @@ osd_client_t::~osd_client_t()
free(ssl_out_buf);
ssl_out_buf = NULL;
}
#endif
}
+13
View File
@@ -231,12 +231,25 @@ struct __attribute__((__packed__)) osd_op_rw_t
uint32_t len;
// flags
// OSD_OP_RETURN_CHAIN for chained reads: return parent number in chain for each block
// read_chain size comes after bitmap, takes 0 bytes / 1 byte / 2 byte / 4 byte per each block,
// depending on the number of parent inodes (0 parents = 0 bytes, up to 255 parents = 1 byte, etc)
uint32_t flags;
// inode metadata revision for chained reads
uint64_t meta_revision;
// object version for atomic "CAS" (compare-and-set) writes
// writes and deletes fail with -EINTR if object version differs from (version-1)
uint64_t version;
static inline size_t chain_info_bytes(size_t chain_size)
{
if (chain_size <= 1)
return 0;
if (chain_size <= 256)
return 1;
if (chain_size <= 65536)
return 2;
return 4;
}
};
struct __attribute__((__packed__)) osd_reply_rw_t
-4
View File
@@ -1,9 +1,7 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#endif
#include <ctype.h>
#include "cli.h"
@@ -628,14 +626,12 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
if (!cfg["enc_key"].is_null())
{
image_creator->set_key = true;
#ifdef WITH_OPENSSL
if (image_creator->enc_key == "random")
{
uint8_t newkey[64];
RAND_bytes(newkey, 64);
image_creator->enc_key = tohexstr(newkey, 64);
}
#endif
else
{
image_creator->enc_key = cfg["enc_key"].string_value();
+2
View File
@@ -286,6 +286,8 @@ struct rm_inode_t
.data = data,
};
}
// Wakeup callers (otherwise they wake up only on next loop())
parent->ringloop->wakeup();
}
in_continue = false;
}
+2 -1
View File
@@ -121,6 +121,7 @@ static const char* help_text =
" --logfile <FILE> log to the specified file\n"
" --enforce 1 enforce permissions at the server side (default is disabled)\n"
" --foreground 1 stay in foreground, do not daemonize\n"
" --trace trace all NFS requests\n"
"\n"
"NFS proxy is stateless if you use immediate_commit=all in your cluster and if\n"
"you do not use client_enable_writeback=true, so you can freely use multiple\n"
@@ -158,7 +159,7 @@ json11::Json::object nfs_proxy_t::parse_args(int narg, const char *args[])
{
const char *opt = args[i]+2;
cfg[str_replace(opt, "-", "_")] = !strcmp(opt, "json") || !strcmp(opt, "block") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") || !strcmp(opt, "trace") ||
!strcmp(opt, "include-empty") || !strcmp(opt, "no-rm") || i == narg-1 ? "1" : args[++i];
}
else
+14 -14
View File
@@ -96,7 +96,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
if (inode_it->second.parent_id == cur_op->req.rw.inode ||
inode_it->second.parent_id == inode_it->second.num ||
chain_size > st_cli.inode_config.size() ||
chain_size > 255)
chain_size > UINT32_MAX)
{
printf("Inode %ju from pool %u has too many parents, returning EINVAL in response to read\n",
INODE_NO_POOL(cur_op->req.rw.inode), INODE_POOL(cur_op->req.rw.inode));
@@ -111,7 +111,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
// Add the original inode
chain_size++;
chain_info_len = (cur_op->req.rw.flags & OSD_OP_RETURN_CHAIN
? (cur_op->req.rw.len / bs_bitmap_granularity)
? osd_op_rw_t::chain_info_bytes(chain_size) * (cur_op->req.rw.len / bs_bitmap_granularity)
: 0);
}
}
@@ -130,9 +130,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
stripe_count * clean_entry_bitmap_size +
// - 'missing' flags for chained reads
(pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 0 : pg_it->second.pg_size)
) +
// read chain info
chain_info_len
)
);
void *data_buf = (uint8_t*)op_data + sizeof(osd_primary_op_data_t);
op_data->pg_num = pg_num;
@@ -140,6 +138,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
op_data->oid = oid;
op_data->stripes = (osd_rmw_stripe_t*)data_buf;
op_data->stripe_count = stripe_count;
op_data->chain_info = NULL;
data_buf = (uint8_t*)data_buf + sizeof(osd_rmw_stripe_t) * stripe_count;
cur_op->op_data = op_data;
if (cur_op->req.hdr.opcode != OSD_OP_SCRUB)
@@ -147,10 +146,17 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
split_stripes(pg_data_size, bs_block_size, (uint32_t)(cur_op->req.rw.offset - oid.stripe), cur_op->req.rw.len, op_data->stripes);
// Resulting bitmaps have to survive op_data and be freed with the op itself
assert(!cur_op->bitmap_buf);
cur_op->bitmap_buf = calloc_or_die(1, clean_entry_bitmap_size * stripe_count);
cur_op->bitmap_buf = (uint8_t*)calloc_or_die(1, clean_entry_bitmap_size*stripe_count + chain_info_len);
uint8_t *buf = cur_op->bitmap_buf;
for (int i = 0; i < stripe_count; i++)
{
op_data->stripes[i].bmp_buf = (uint8_t*)cur_op->bitmap_buf + clean_entry_bitmap_size * i;
op_data->stripes[i].bmp_buf = buf;
buf += clean_entry_bitmap_size;
if (i == pg_data_size-1 && chain_info_len)
{
op_data->chain_info = buf;
buf += chain_info_len;
}
}
}
op_data->chain_size = chain_size;
@@ -164,11 +170,6 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
data_buf = (uint8_t*)data_buf + chain_size * stripe_count * clean_entry_bitmap_size;
op_data->missing_flags = (uint8_t*)data_buf;
data_buf = (uint8_t*)data_buf + chain_size * (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 0 : pg_it->second.pg_size);
if (chain_info_len)
{
op_data->chain_info = (uint8_t*)data_buf;
data_buf = (uint8_t*)data_buf + chain_info_len;
}
// Copy chain
int chain_num = 0;
op_data->read_chain[chain_num] = cur_op->req.rw.inode;
@@ -319,6 +320,7 @@ resume_2:
}
cur_op->reply.rw.version = op_data->fact_ver;
cur_op->reply.rw.bitmap_len = (pg ? pg->pg_data_size : 1) * clean_entry_bitmap_size;
cur_op->bitmap = op_data->stripes[0].bmp_buf;
if (op_data->degraded)
{
// Reconstruct missing stripes
@@ -331,7 +333,6 @@ resume_2:
{
reconstruct_stripes_ec(stripes, pg->pg_size, pg->pg_data_size, clean_entry_bitmap_size);
}
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
for (int role = 0; role < pg->pg_size; role++)
{
if (stripes[role].req_end != 0)
@@ -346,7 +347,6 @@ resume_2:
}
else
{
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
cur_op->iov.push_back(cur_op->buf, cur_op->req.rw.len);
}
finish_op(cur_op, cur_op->req.rw.len);
+1 -1
View File
@@ -56,7 +56,7 @@ struct osd_primary_op_data_t
int chain_size;
osd_chain_read_t *chain_reads;
int chain_read_count;
uint8_t *chain_info;
void *chain_info;
};
};
};
+10 -3
View File
@@ -582,11 +582,10 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
}
}
// Send bitmap
cur_op->bitmap = op_data->stripes[0].bmp_buf;
cur_op->reply.rw.bitmap_len = (pg ? pg->pg_data_size : 1) * clean_entry_bitmap_size;
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
if (cur_op->req.rw.flags & OSD_OP_RETURN_CHAIN)
{
cur_op->iov.push_back(op_data->chain_info, (cur_op->req.rw.len / bs_bitmap_granularity));
cur_op->reply.rw.bitmap_len += (cur_op->req.rw.len / bs_bitmap_granularity);
}
// And finally compose the result
@@ -596,6 +595,7 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
int prev = (cur_op->req.rw.offset - op_data->oid.stripe) / bs_bitmap_granularity;
int end = prev + cur_op->req.rw.len/bs_bitmap_granularity;
int cur = prev;
size_t key_index_bytes = osd_op_rw_t::chain_info_bytes(op_data->chain_size);
while (cur <= end)
{
bool has_bit = false;
@@ -607,7 +607,14 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
if (has_bit)
{
if (op_data->chain_info)
op_data->chain_info[cur] = pos;
{
if (key_index_bytes == 1)
((uint8_t*)op_data->chain_info)[cur] = pos;
else if (key_index_bytes == 2)
((uint16_t*)op_data->chain_info)[cur] = pos;
else
((uint32_t*)op_data->chain_info)[cur] = pos;
}
break;
}
}
+2
View File
@@ -83,6 +83,8 @@ void osd_t::finish_op(osd_op_t *cur_op, int retval)
rm_inflight(pg);
}
assert(!cur_op->op_data->subops);
free(cur_op->op_data);
cur_op->op_data = NULL;
}
cur_op->reply.hdr.magic = SECONDARY_OSD_REPLY_MAGIC;
cur_op->reply.hdr.id = cur_op->req.hdr.id;
+1 -1
View File
@@ -413,7 +413,7 @@ void osd_t::submit_scrub_subops(osd_op_t *cur_op)
}
}
assert(!cur_op->bitmap_buf);
cur_op->bitmap_buf = calloc_or_die(1, clean_entry_bitmap_size * op_data->stripe_count);
cur_op->bitmap_buf = (uint8_t*)calloc_or_die(1, clean_entry_bitmap_size * op_data->stripe_count);
for (int i = 0; i < op_data->stripe_count; i++)
{
op_data->stripes[i].bmp_buf = (uint8_t*)cur_op->bitmap_buf + clean_entry_bitmap_size * i;
-8
View File
@@ -62,14 +62,6 @@ target_link_libraries(test_cas
vitastor_client
)
# http_hello
add_executable(http_hello
http_hello.cpp
)
target_link_libraries(http_hello
vitastor_client
)
# test_crc32
add_executable(test_crc32
test_crc32.cpp
-61
View File
@@ -1,61 +0,0 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdexcept>
#include "http_client.h"
#include "ringloop.h"
#include "epoll_manager.h"
#include "addr_util.h"
int main(int narg, char *args[])
{
ring_consumer_t looper;
ring_loop_t *ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
epoll_manager_t *epmgr = new epoll_manager_t(ringloop);
// Accept new connections
int listen_fd = create_and_bind_socket("0.0.0.0", 8085, 128, NULL);
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
std::string error;
auto http_ctx = http_context_init(epmgr->tfd, "", "", "", false, error);
epmgr->set_fd_handler(listen_fd, false, [http_ctx](int listen_fd, int events)
{
sockaddr_storage addr;
socklen_t peer_addr_size = sizeof(addr);
int peer_fd;
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
{
assert(peer_fd != 0);
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
int one = 1;
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
auto co = http_init(http_ctx);
http_serve(co, peer_fd, (http_options_t){}, [co](http_message_t *msg)
{
if (msg->eof)
{
http_destroy(co);
return;
}
http_reply(co, "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Length: 13\r\n\r\nHello, world!");
});
}
});
while (true)
{
ringloop->loop();
ringloop->wait();
}
delete epmgr;
delete ringloop;
return 0;
}
+5 -11
View File
@@ -1,9 +1,7 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#endif
#include <stdio.h>
#include <stdlib.h>
@@ -556,7 +554,6 @@ void test_writeback_merge()
printf("[ok] writeback merge test\n");
}
#ifdef WITH_OPENSSL
void test_msgr_encrypt()
{
const size_t sz = 1048576;
@@ -595,7 +592,7 @@ void test_msgr_encrypt()
enc->start(key, 4096 * 114, 4096);
in_pos = out_pos = 0;
enc->update(src+4096, 4096, crypt2, 4095, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 4095);
enc->update(src+4096+in_pos, 4096-in_pos, crypt2+out_pos, 4096-out_pos, in_pos, out_pos);
assert(in_pos == 4096);
@@ -626,10 +623,10 @@ void test_msgr_encrypt()
assert(in_pos == 3000);
assert(out_pos == 0);
dec->update(crypt+4096+3000, 3000, decrypt, 500, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 500);
dec->update(crypt+4096+in_pos, 6000-in_pos, decrypt+out_pos, 3000, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 3500);
dec->update(crypt+4096+in_pos, 6000-in_pos, decrypt+out_pos, 1000, in_pos, out_pos);
assert(in_pos == 4096);
@@ -638,10 +635,10 @@ void test_msgr_encrypt()
assert(in_pos == 6000);
assert(out_pos == 4096);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 4500-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
assert(in_pos == 8191);
assert(out_pos == 4500);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 7500-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
assert(in_pos == 8191);
assert(out_pos == 7500);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 8192-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
@@ -725,7 +722,6 @@ void test_msgr_decrypt_chain()
free(src);
printf("[ok] msgr aes-xts chained decrypt\n");
}
#endif
void test_vault()
{
@@ -794,10 +790,8 @@ int main(int narg, char *args[])
test2();
test_writeback();
test_writeback_merge();
#ifdef WITH_OPENSSL
test_msgr_encrypt();
test_msgr_decrypt_chain();
#endif
test_vault();
return 0;
}
+2 -1
View File
@@ -1893,7 +1893,8 @@ void test_big_intent_csums()
heap.finish_recheck();
auto mod = heap.get_recheck_modified_blocks();
assert(mod.size() == 0);
assert(mod.size() == 1);
assert(mod[0] == 0);
// read object 1 - big_intent should be there
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
+119
View File
@@ -0,0 +1,119 @@
// 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 <openssl/ssl.h>
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;
}
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)
{
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);
}
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;
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#pragma once
#include <string>
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#endif
X509 *openssl_load_cert(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);
+9 -4
View File
@@ -535,12 +535,12 @@ std::string urldecode(const std::string & orig)
return res;
}
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
size_t fromhexstr(const char *from, size_t from_len, uint8_t *to, size_t to_len)
{
if (bytes > from.size()/2)
bytes = from.size()/2;
if (to_len > from_len/2)
to_len = from_len/2;
size_t i = 0;
while (i < bytes)
while (i < to_len)
{
uint8_t x = fromhexchar(from[2*i], 16);
uint8_t y = fromhexchar(from[2*i+1], 16);
@@ -552,6 +552,11 @@ size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
return i;
}
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
{
return fromhexstr(from.data(), from.size(), to, bytes);
}
std::string tohexstr(const uint8_t *from, size_t bytes)
{
std::string res;
+1
View File
@@ -35,6 +35,7 @@ std::string realpath_str(std::string path, bool nofail = true);
std::string format_datetime(uint64_t unixtime);
bool is_zero(void *buf, size_t size);
std::string urldecode(const std::string & orig);
size_t fromhexstr(const char *from, size_t from_len, uint8_t *to, size_t to_len);
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to);
std::string tohexstr(const uint8_t *from, size_t bytes);
bool ishexstr(const std::string & str);
+2
View File
@@ -34,6 +34,8 @@ extern "C" {
# define XXH_NOESCAPE
#endif
#define XXH_SECRET_DEFAULT_SIZE 192
typedef enum {
XXH_OK = 0,
XXH_ERROR
+18
View File
@@ -27,6 +27,7 @@ ETCD_COUNT=${ETCD_COUNT:-1}
ANTIETCD=${ANTIETCD}
USE_RAMDISK=${USE_RAMDISK}
ETCD_SCHEME=${ETCD_SCHEME:-http}
OSD_TLS=${OSD_TLS:-1}
RAMDISK=/run/user/$(id -u)
findmnt $RAMDISK >/dev/null || (sudo mkdir -p $RAMDISK && sudo mount -t tmpfs tmpfs $RAMDISK)
@@ -124,6 +125,23 @@ VITASTOR_CFG='"etcd_address":"'$ETCD_URL'"'"$VITASTOR_CFG"
if [[ "$ETCD_SCHEME" = "https" ]]; then
VITASTOR_CFG="$VITASTOR_CFG"',"etcd_ca":"'$(pwd)'/testdata/etcd.crt"'
fi
if [[ "$OSD_TLS" = "1" ]]; then
cd ./testdata
openssl req -days 3650 -x509 -new -newkey rsa:4096 -nodes -keyout client_ca.key -out client_ca.crt \
-subj '/C=RU/ST=Russia/L=Moscow/O=VitastorClientCA'
openssl req -days 3650 -x509 -new -newkey rsa:4096 -nodes -keyout osd.key -out osd.crt \
-subj '/C=RU/ST=Russia/L=Moscow/O=VitastorOSD' -addext "extendedKeyUsage = serverAuth, clientAuth"
openssl req -subj '/CN=test' -nodes -new -keyout cli.key -out cli.csr -addext "extendedKeyUsage = clientAuth"
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in cli.csr -out cli.crt
rm cli.csr
cd $(dirname $0)/..
VITASTOR_CFG="$VITASTOR_CFG"',"osd_tls_cert":"'$(pwd)'/testdata/osd.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"osd_tls_key":"'$(pwd)'/testdata/osd.key"'
VITASTOR_CFG="$VITASTOR_CFG"',"osd_tls_ca":"'$(pwd)'/testdata/osd.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"client_tls_ca":"'$(pwd)'/testdata/client_ca.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"tls_cert":"'$(pwd)'/testdata/cli.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"tls_key":"'$(pwd)'/testdata/cli.key"'
fi
echo "{$VITASTOR_CFG}" > ./testdata/vitastor.conf
VITASTOR_CFG=./testdata/vitastor.conf
VITASTOR_CLI="build/src/cmd/vitastor-cli --config_path $VITASTOR_CFG"