Skip to content

Commit

Permalink
Added TLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Jan 13, 2025
1 parent e0dbde4 commit 67d5a2c
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .cproject
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1001322505" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.388694789" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.448165414" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.preprocessor.def.symbols.1635366782" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols">
<listOptionValue builtIn="false" value="WITH_TLS=1"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1125534563" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.base.47207540" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base">
Expand Down
2 changes: 1 addition & 1 deletion .settings/language.settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="([^/\\\\]*)((g?cc)|([gc]\+\+)|(clang))" prefer-non-shared="true"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="725214692103932884" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="725523575559932884" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
Expand Down
6 changes: 6 additions & 0 deletions .settings/org.eclipse.cdt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
doxygen/doxygen_new_line_after_brief=true
doxygen/doxygen_use_brief_tag=false
doxygen/doxygen_use_javadoc_tags=true
doxygen/doxygen_use_pre_tag=false
doxygen/doxygen_use_structural_commands=false
eclipse.preferences.version=1
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ distclean: clean

SOURCES = $(SRC)/smax.c $(SRC)/smax-easy.c $(SRC)/smax-lazy.c $(SRC)/smax-queue.c \
$(SRC)/smax-meta.c $(SRC)/smax-messages.c $(SRC)/smax-resilient.c \
$(SRC)/smax-util.c $(SRC)/procname.c
$(SRC)/smax-util.c $(SRC)/smax-tls.c $(SRC)/procname.c

# Generate a list of object (obj/*.o) files from the input sources
OBJECTS := $(subst $(SRC),$(OBJ),$(SOURCES))
Expand Down
22 changes: 22 additions & 0 deletions config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,29 @@ CHECKOPTS += --inline-suppr $(CHECKEXTRA)
# Specific Doxygen to use if not the default one
#DOXYGEN ?= /opt/bin/doxygen

# Whether to build with TLS support (via OpenSSL). If not defined, we'll
# enable it automatically if libssl is available
#WITH_TLS = 1

# ============================================================================
# END of user config section.
#
# Below are some generated constants based on the one that were set above
# ============================================================================

ifneq ($(shell which ldconfig), )
# Detect OpenSSL automatically, and enable TLS support if present
ifndef WITH_TLS
ifneq ($(shell ldconfig -p | grep libssl), )
$(info INFO: TLS support is enabled automatically.)
WITH_TLS = 1
else
$(info INFO: optional TLS support is not enabled.)
WITH_TLS = 0
endif
endif
endif

ifeq ($(NO_PROCNAME),1)
CPPFLAGS += -DNO_PROCNAME=1
endif
Expand All @@ -91,6 +108,11 @@ ifdef NETFLAGS
LDFLAGS += $(NETFLAGS)
endif

ifeq ($(WITH_TLS),1)
CPPFLAGS += -DWITH_TLS=1
LDFLAGS += -lssl
endif

# Link against pthread and dependencies
LDFLAGS += -lpthread -lredisx -lxchange

Expand Down
1 change: 1 addition & 0 deletions include/smax-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ int smaxUnlockConfig();
int smaxLockNotify();
int smaxUnlockNotify();

int smaxConfigTLSAsync(Redis *redis);
long smaxGetHash(const char *buf, int size);

int smaxRead(PullRequest *req, int channel);
Expand Down
9 changes: 9 additions & 0 deletions include/smax.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ int smaxSetAuth(const char *username, const char *password);
int smaxSetDB(int idx);
int smaxSetTcpBuf(int size);

int smaxSetTLS(const char *ca_path, const char *ca_file);
int smaxDisableTLS();
int smaxSetTLSVerify(boolean value);
int smaxSetMutualTLS(const char *cert_file, const char *key_file);
int smaxSetTLSServerName(const char *host);
int smaxSetTLSCiphers(const char *cipher_list);
int smaxSetTLSCipherSuites(const char *list);
int smaxSetDHCipherParams(const char *dh_params_file);

int smaxConnect();
int smaxConnectTo(const char *server);
int smaxDisconnect();
Expand Down
268 changes: 268 additions & 0 deletions src/smax-tls.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/**
* @file
*
* @date Created on Jan 13, 2025
* @author Attila Kovacs
*
* TLS configuration for SMA-X.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "smax-private.h"

/// \cond PRIVATE
#if WITH_TLS
typedef struct {
boolean enabled; ///< Whether TLS is enabled.
char *ca_path; ///< Directory in which CA certificates reside
char *ca_certificate; ///< CA sertificate
boolean skip_verify; ///< Whether to skip verification of the certificate (insecure)
char *certificate; ///< Client certificate (mutual TLS only)
char *key; ///< Client private key (mutual TLS only)
char *dh_params; ///< (optional) parameter file for DH based ciphers
char *ciphers; ///< colon separated list of ciphers to try (TKS v1.2 and earlier)
char *cipher_suites; ///< colon separated list of ciphers suites to try (TKS v1.3 and later)
char *hostname; ///< Server name for SNI
} TLSConfig;

static TLSConfig config;
#endif

/**
* (<i>for internal use<i>) Applies the TLS configration for the SMA-X server. The caller
* should have a lock on the SMA-X configuration mutex, and it should be called after the
* Redis instance is initialized, but before it is connected.
*
* @param redis The unconnected Redis instance to be used for SMA-X.
* @return X_SUCCESS (0) if successful, or else and error code &lt;0.
*
* @sa smaxSetTLS()
*/
int smaxConfigTLSAsync(Redis *redis) {
#if WITH_TLS
static const char *fn = "smaxConfigTLS()";

if(!redis) return x_error(X_NULL, EINVAL, fn, "redis is NULL");

if(!config.enabled) return X_SUCCESS;

prop_error(fn, redisxSetTLS(redis, config.ca_path, config.ca_certificate));
prop_error(fn, redisxSetTLSVerify(redis, !config.skip_verify));
prop_error(fn, redisxSetMutualTLS(redis, config.certificate, config.key));
prop_error(fn, redisxSetTLSServerName(redis, config.hostname));
prop_error(fn, redisxSetTLSCiphers(redis, config.ciphers));
prop_error(fn, redisxSetTLSCipherSuites(redis, config.cipher_suites));
prop_error(fn, redisxSetDHCipherParams(redis, config.dh_params));
#endif
(void) redis;
return X_SUCCESS;
}
/// \endcond


/**
* Configures a TLS-encrypted connection to thr SMA-X server with the specified CA certificate file.
* Normally you will want to set up mutual TLS with smaxSetMutualTLS() also, unless the server is not
* requiring mutual authentication. Additionally, you might also want to set parameters for DH-based
* cyphers if needed using smaxSetDHCypherParams().
*
* @param ca_path Directory containing CA certificates. It may be NULL to use the default locations.
* @param ca_file CA certificate file relative to specified directory. It may be NULL to use default
* certificate.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built
* without TLS support.
*
* @sa smaxDisableTLS()
* @sa smaxSetMutualTLS()
* @sa smaxSetDHCipherParams()
* @sa smaxSetTLSCiphers()
* @sa smaxSetTLSCipherSuites()
* @sa smaxSetTLSServerName()
* @sa smaxSetTLSVerify()
*/
int smaxSetTLS(const char *ca_path, const char *ca_file) {
#if WITH_TLS
smaxLockConfig();
if(config.ca_path) free(config.ca_path);
config.ca_path = xStringCopyOf(ca_path);
if(config.ca_certificate) free(config.ca_certificate);
config.ca_certificate = xStringCopyOf(ca_file);
config.enabled = TRUE;
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) ca_path;
(void) ca_file;
return x_error(X_FAILURE, ENOSYS, "smaxSetTLS", "smax-clib was built without TLS support");
#endif
}

/**
* Disables a previously enabled TLS configuration for SMA-X.
*
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built without TLS
* support.
*
* @sa smaxSetTLS()
*/
int smaxDisableTLS() {
#if WITH_TLS
smaxLockConfig();
config.enabled = FALSE;
smaxUnlockConfig();
#endif
return X_SUCCESS;
}

/**
* Sets whether to verify the the certificate. Certificates are verified by default.
*
* @param value TRUE (non-zero) to verify certificates, or else FALSE (0).
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built without TLS
* support.
*
* @sa smaxSetTLS()
*/
int smaxSetTLSVerify(boolean value) {
#if WITH_TLS
smaxLockConfig();
config.skip_verify = !value;
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) value;
return x_error(X_FAILURE, ENOSYS, "smaxSetTLSVerify", "smax-clib was built without TLS support");
#endif
}


/**
* Set a TLS certificate and private key for mutual TLS. You will still need to call smaxSetTLS() also to create a
* complete TLS configuration. Redis normally uses mutual TLS, which requires both the client and the server to
* authenticate themselves. For this you need the server's TLS certificate and private key also. It is possible to
* configure Redis servers to verify one way only with a CA certificate, in which case you don't need to call this to
* configure the client.
*
* To disable mutual TLS, set both file name arguments to NULL.
*
* @param cert_file Path to the server's certificate file.
* @param key_file Path to the server'sprivate key file.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built without TLS
* support.
*
* @sa smaxSetTLS()
*/
int smaxSetMutualTLS(const char *cert_file, const char *key_file) {
#if WITH_TLS
smaxLockConfig();
if(config.certificate) free(config.certificate);
config.certificate = xStringCopyOf(cert_file);
if(config.key) free(config.key);
config.key = xStringCopyOf(key_file);
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) cert_file;
(void) key_file;
return x_error(X_FAILURE, ENOSYS, "smaxSetMutualTLS", "smax-clib was built without TLS support");
#endif
}

/**
* Sets the Server name for TLS Server Name Indication (SNI), an optional extra later of security.
*
* @param host server name to use for SNI.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built without TLS
* support.
*
* @sa smaxSetTLS()
*/
int smaxSetTLSServerName(const char *host) {
#if WITH_TLS
smaxLockConfig();
if(config.hostname) free(config.hostname);
config.hostname = xStringCopyOf(host);
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) serverName;
return x_error(X_FAILURE, ENOSYS, "smaxSetTLSServerName", "smax-clib was built without TLS support");
#endif
}

/**
* Sets the TLS ciphers to try (TLSv1.2 and earlier).
*
* @param cipher_list a colon (:) separated list of ciphers, or NULL for default ciphers.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built
* without TLS support.
*
* @sa smaxSetTLSCipherSuites()
* @sa smaxSetTLS()
* @sa smaSetDHCipherParams()
*/
int smaxSetTLSCiphers(const char *cipher_list) {
#if WITH_TLS
smaxLockConfig();
if(config.ciphers) free(config.ciphers);
config.ciphers = xStringCopyOf(cipher_list);
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) cipher_list;
return x_error(X_FAILURE, ENOSYS, "smaxSetTLSCiphers", "smax-clib was built without TLS support");
#endif
}

/**
* Sets the TLS ciphers suites to try (TLSv1.3 and later).
*
* @param list a colon (:) separated list of cipher suites, or NULL for default cipher suites.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built
* without TLS support.
*
* @sa smaxSetTLSCiphers()
* @sa smaxSetTLS()
* @sa smaxSetDHCipherParams()
*/
int smaxSetTLSCipherSuites(const char *list) {
#if WITH_TLS
smaxLockConfig();
if(config.cipher_suites) free(config.cipher_suites);
config.cipher_suites = xStringCopyOf(list);
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) list;
return x_error(X_FAILURE, ENOSYS, "smaxSetTLSCipherSuites", "smax-clib was built without TLS support");
#endif
}

/**
* Sets parameters for DH-based cyphers when using a TLS encrypted connection.
*
* @param dh_params_file Path to the DH-based cypher parameters file (in PEM format; we don't support
* the old DER format), or NULL for no params.
* @return X_SUCCESS (0) if successful, or else X_FAILURE (-1) if the SMA-X library was built
* without TLS support.
*
* @sa smaxSetTLS()
* @sa smaxSetTLSCiphers()
* @sa smaxSetTLSCipherSuites()
*/
int smaxSetDHCipherParams(const char *dh_params_file) {
#if WITH_TLS
smaxLockConfig();
if(config.dh_params) free(config.dh_params);
config.dh_params = xStringCopyOf(dh_params_file);
smaxUnlockConfig();
return X_SUCCESS;
#else
(void) dh_params_file;
return x_error(X_FAILURE, ENOSYS, "smaxSetDHCipherParams", "smax-clib was built without TLS support");
#endif
}
Loading

0 comments on commit 67d5a2c

Please sign in to comment.