nDPId/ncrypt.c
Toni Uhlig 5d5c6c732c
Some checks failed
ArchLinux PKGBUILD / build (push) Has been cancelled
Docker Build / docker (push) Has been cancelled
FreeBSD Build / Build and Test (push) Has been cancelled
Build OpenWrt Packages / build (armsr-armv8) (push) Has been cancelled
Build OpenWrt Packages / build (malta-be) (push) Has been cancelled
Build OpenWrt Packages / build (ramips-mt7621) (push) Has been cancelled
Build OpenWrt Packages / build (x86-64) (push) Has been cancelled
RPM Build / build (push) Has been cancelled
Build / macOS-14 cc (push) Has been cancelled
Build / ubuntu-latest clang (push) Has been cancelled
Build / ubuntu-latest clang-1 (push) Has been cancelled
Build / ubuntu-22.04 clang-12 (push) Has been cancelled
Build / ubuntu-latest gcc (push) Has been cancelled
Build / ubuntu-latest gcc-1 (push) Has been cancelled
Build / ubuntu-latest gcc-2 (push) Has been cancelled
Build / ubuntu-22.04 gcc-10 (push) Has been cancelled
Build / ubuntu-22.04 gcc-9 (push) Has been cancelled
SonarQube Scan / Build and analyze (push) Has been cancelled
Add TLS CRL option
* harden OpenSSL usage
 * CRL: init/script
 * nDPIsrvd.h: increase max JSON tokens

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2026-10-06 07:17:55 +02:00

421 lines
12 KiB
C

#include "ncrypt.h"
#include <endian.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <unistd.h>
#include "utils.h"
char const *
ncrypt_strerror(int ncrypt_error)
{
switch (ncrypt_error) {
case NCRYPT_SUCCESS:
return "Success";
case NCRYPT_NOT_INITIALIZED:
return "Not Initialized";
case NCRYPT_ALREADY_INITIALIZED:
return "Already Initialized";
case NCRYPT_NULL_PTR:
return "NULL Pointer";
case NCRYPT_PEM_LOAD_FAILED:
return "PEM Load Failure";
case NCRYPT_WANT_READ:
return "Require Input";
case NCRYPT_WANT_WRITE:
return "Require Output";
case NCRYPT_HANDSHAKE_FAILED:
return "Handshake Failure";
case NCRYPT_IO_ERROR:
return "Input/Output Error";
}
return "Unknown Crypto Error";
}
int ncrypt_init(void)
{
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL) != 1)
{
return NCRYPT_NOT_INITIALIZED;
}
//ERR_print_errors_fp(stderr);
return NCRYPT_SUCCESS;
}
static int ncrypt_init_ctx(struct ncrypt_ctx * const ctx, SSL_METHOD const * const meth)
{
if (meth == NULL)
{
return NCRYPT_NULL_PTR;
}
if (ctx->ssl_ctx != NULL)
{
return NCRYPT_ALREADY_INITIALIZED;
}
ctx->ssl_ctx = SSL_CTX_new(meth);
if (ctx->ssl_ctx == NULL)
{
return NCRYPT_NOT_INITIALIZED;
}
SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_RENEGOTIATION | SSL_OP_NO_COMPRESSION | SSL_OP_NO_TICKET);
SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ctx->ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_ciphersuites(ctx->ssl_ctx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256");
return NCRYPT_SUCCESS;
}
static int ncrypt_load_crl(struct ncrypt_ctx * const ctx, char const * const crl_pem_path)
{
if (crl_pem_path == NULL)
{
return NCRYPT_SUCCESS;
}
X509_STORE * const store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
if (store == NULL)
{
return NCRYPT_NOT_INITIALIZED;
}
X509_LOOKUP * const lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup == NULL ||
X509_load_crl_file(lookup, crl_pem_path, X509_FILETYPE_PEM) <= 0)
{
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
logger_early(1, "Failed to load CRL `%s': %s", crl_pem_path, err_buf);
while (ERR_get_error() != 0) {}
return NCRYPT_PEM_LOAD_FAILED;
}
if (X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK) != 1)
{
return NCRYPT_PEM_LOAD_FAILED;
}
return NCRYPT_SUCCESS;
}
static int ncrypt_load_pems(struct ncrypt_ctx * const ctx,
char const * const ca_path,
char const * const privkey_pem_path,
char const * const cert_pem_path,
char const * const crl_path)
{
char err_buf[256];
err_buf[0] = '\0';
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, cert_pem_path, SSL_FILETYPE_PEM) <= 0) {
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
logger_early(1, "Failed to load Certificate `%s': %s", cert_pem_path, err_buf);
while (ERR_get_error() != 0) {}
}
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, privkey_pem_path, SSL_FILETYPE_PEM) <= 0) {
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
logger_early(1, "Failed to load Private Key `%s': %s", privkey_pem_path, err_buf);
while (ERR_get_error() != 0) {}
}
if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_path, NULL) <= 0)
{
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
logger_early(1, "Failed to load CA File `%s': %s", ca_path, err_buf);
while (ERR_get_error() != 0) {}
}
if (err_buf[0] != '\0') {
return NCRYPT_PEM_LOAD_FAILED;
}
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
logger_early(1, "Private Key Verification failed: %s", err_buf);
while (ERR_get_error() != 0) {}
return NCRYPT_PEM_LOAD_FAILED;
}
if (crl_path != NULL) {
int rv = ncrypt_load_crl(ctx, crl_path);
if (rv != NCRYPT_SUCCESS) {
return rv;
}
}
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_verify_depth(ctx->ssl_ctx, 1);
return NCRYPT_SUCCESS;
}
int ncrypt_init_client(struct ncrypt_ctx * const ctx,
char const * const ca_path,
char const * const privkey_pem_path,
char const * const cert_pem_path,
char const * const crl_path)
{
if (ca_path == NULL || privkey_pem_path == NULL || cert_pem_path == NULL)
{
return NCRYPT_NULL_PTR;
}
int rv = ncrypt_init_ctx(ctx, TLS_client_method());
if (rv != NCRYPT_SUCCESS)
{
return rv;
}
return ncrypt_load_pems(ctx, ca_path, privkey_pem_path, cert_pem_path, crl_path);
}
int ncrypt_init_server(struct ncrypt_ctx * const ctx,
char const * const ca_path,
char const * const privkey_pem_path,
char const * const cert_pem_path,
char const * const crl_path)
{
if (ca_path == NULL || privkey_pem_path == NULL || cert_pem_path == NULL)
{
return NCRYPT_NULL_PTR;
}
int rv = ncrypt_init_ctx(ctx, TLS_server_method());
if (rv != NCRYPT_SUCCESS)
{
return rv;
}
SSL_CTX_set_num_tickets(ctx->ssl_ctx, 0);
return ncrypt_load_pems(ctx, ca_path, privkey_pem_path, cert_pem_path, crl_path);
}
int ncrypt_on_connect(struct ncrypt_ctx * const ctx, int connect_fd, struct ncrypt_entity * const ent)
{
if (ent->ssl == NULL)
{
ent->ssl = SSL_new(ctx->ssl_ctx);
if (ent->ssl == NULL)
{
ent->last_ncrypt_error = NCRYPT_NOT_INITIALIZED;
return NCRYPT_NOT_INITIALIZED;
}
SSL_set1_host(ent->ssl, "nDPIsrvd");
SSL_set_purpose(ent->ssl, X509_PURPOSE_SSL_CLIENT);
SSL_set_hostflags(ent->ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
SSL_set_fd(ent->ssl, connect_fd);
SSL_set_connect_state(ent->ssl);
}
int rv = SSL_do_handshake(ent->ssl);
if (rv != 1)
{
int err = SSL_get_error(ent->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE)
{
ent->last_ncrypt_error = NCRYPT_WANT_WRITE;
return NCRYPT_WANT_WRITE;
}
else if (err == SSL_ERROR_WANT_READ)
{
ent->last_ncrypt_error = NCRYPT_WANT_READ;
return NCRYPT_WANT_READ;
}
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
if (SSL_get_verify_result(ent->ssl) != X509_V_OK) {
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
return NCRYPT_SUCCESS;
}
int ncrypt_on_accept(struct ncrypt_ctx * const ctx, int accept_fd, struct ncrypt_entity * const ent)
{
if (ent->ssl == NULL)
{
ent->ssl = SSL_new(ctx->ssl_ctx);
if (ent->ssl == NULL)
{
ent->last_ncrypt_error = NCRYPT_NOT_INITIALIZED;
return NCRYPT_NOT_INITIALIZED;
}
SSL_set_fd(ent->ssl, accept_fd);
SSL_set_accept_state(ent->ssl);
SSL_set_purpose(ent->ssl, X509_PURPOSE_SSL_SERVER);
}
int rv = SSL_accept(ent->ssl);
if (rv != 1)
{
int err = SSL_get_error(ent->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE)
{
ent->last_ncrypt_error = NCRYPT_WANT_WRITE;
return NCRYPT_WANT_WRITE;
}
else if (err == SSL_ERROR_WANT_READ)
{
ent->last_ncrypt_error = NCRYPT_WANT_READ;
return NCRYPT_WANT_READ;
}
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
if (SSL_get_verify_result(ent->ssl) != X509_V_OK) {
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
X509 * const peer = SSL_get1_peer_certificate(ent->ssl);
if (peer == NULL)
{
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
//PEM_write_X509(stderr, peer);
int matched = 0;
STACK_OF(GENERAL_NAME) *sans =
X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL);
if (sans != NULL)
{
int n = sk_GENERAL_NAME_num(sans);
for (int i = 0; i < n && !matched; i++)
{
const GENERAL_NAME *gn = sk_GENERAL_NAME_value(sans, i);
if (gn->type != GEN_DNS)
continue;
const unsigned char *data = ASN1_STRING_get0_data(gn->d.dNSName);
int len = ASN1_STRING_length(gn->d.dNSName);
if (len <= 0 || memchr(data, 0, (size_t)len) != NULL)
continue;
if ((size_t)len == strlen("collector") &&
memcmp(data, "collector", (size_t)len) == 0)
{
ent->is_collector = 0b1;
matched = 1;
}
else if ((size_t)len == strlen("distributor") &&
memcmp(data, "distributor", (size_t)len) == 0)
{
ent->is_distributor = 0b1;
matched = 1;
}
}
sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);
}
X509_free(peer);
if (!matched)
{
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return NCRYPT_HANDSHAKE_FAILED;
}
return NCRYPT_SUCCESS;
}
ssize_t ncrypt_read(struct ncrypt_entity * const ent, char * const json_msg, int json_msg_len)
{
if (ent->ssl == NULL)
{
ent->last_ncrypt_error = NCRYPT_NOT_INITIALIZED;
return -1;
}
if (ncrypt_handshake_done(ent) == 0)
{
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return -1;
}
int rv = SSL_read(ent->ssl, json_msg, json_msg_len);
if (rv <= 0)
{
int err = SSL_get_error(ent->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE)
{
ent->last_ncrypt_error = NCRYPT_WANT_WRITE;
}
else if (err == SSL_ERROR_WANT_READ)
{
ent->last_ncrypt_error = NCRYPT_WANT_READ;
}
else
{
ent->last_ncrypt_error = NCRYPT_IO_ERROR;
}
return -1;
}
return rv;
}
ssize_t ncrypt_write(struct ncrypt_entity * const ent, char const * const json_msg, int json_msg_len)
{
if (ent->ssl == NULL)
{
ent->last_ncrypt_error = NCRYPT_NOT_INITIALIZED;
return -1;
}
if (ncrypt_handshake_done(ent) == 0)
{
ent->last_ncrypt_error = NCRYPT_HANDSHAKE_FAILED;
return -1;
}
int rv = SSL_write(ent->ssl, json_msg, json_msg_len);
if (rv <= 0)
{
int err = SSL_get_error(ent->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE)
{
ent->last_ncrypt_error = NCRYPT_WANT_WRITE;
}
else if (err == SSL_ERROR_WANT_READ)
{
ent->last_ncrypt_error = NCRYPT_WANT_READ;
}
else
{
ent->last_ncrypt_error = NCRYPT_IO_ERROR;
}
return -1;
}
return rv;
}
void ncrypt_free_entity(struct ncrypt_entity * const ent)
{
if (ent->ssl != NULL) {
SSL_shutdown(ent->ssl);
SSL_free(ent->ssl);
}
ent->ssl = NULL;
}
void ncrypt_free_ctx(struct ncrypt_ctx * const ctx)
{
SSL_CTX_free(ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
}