diff --git a/README.md b/README.md index 1a48d65..f4f042b 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,9 @@ systemd-netlogd reads configuration files named `/etc/systemd/netlogd.conf` and Takes a time span value. The default unit is seconds, but other units may be specified, see systemd.time(5). Defaults to 30 seconds and must not be smaller than 1 second. + TLSCertificateAuthMode= + Specifies whether to validate the certificate. Takes one of no, allow, deny, warn. Defaults to 'no' which disables certificate validation. + Optional settings StructuredData= diff --git a/doc/index.rst b/doc/index.rst index 48a0a9d..57fc799 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -50,6 +50,9 @@ This will create a user systemd-journal-netlog Takes a time span value. The default unit is seconds, but other units may be specified, see systemd.time(5). Defaults to 30 seconds and must not be smaller than 1 second. +| ``TLSCertificateAuthMode=`` + Specifies whether to validate the certificate. Takes one of no, allow, deny, warn. Defaults to 'no' which disables certificate validation. + | | | Optional settings diff --git a/src/netlog/netlog-conf.c b/src/netlog/netlog-conf.c index 7ddc08e..7822fa0 100644 --- a/src/netlog/netlog-conf.c +++ b/src/netlog/netlog-conf.c @@ -92,6 +92,35 @@ int config_parse_log_format(const char *unit, return 0; } +int config_parse_tls_certificate_auth_mode(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + assert(m); + + r = certificate_auth_mode_from_string(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, -r, "Failed to parse '%s=%s', ignoring.", lvalue, rvalue); + return 0; + } + + m->auth_mode = r; + return 0; +} + int config_parse_namespace(const char *unit, const char *filename, unsigned line, diff --git a/src/netlog/netlog-conf.h b/src/netlog/netlog-conf.h index 95b85a1..138c614 100644 --- a/src/netlog/netlog-conf.h +++ b/src/netlog/netlog-conf.h @@ -40,6 +40,17 @@ int config_parse_log_format(const char *unit, void *data, void *userdata); +int config_parse_tls_certificate_auth_mode(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + int config_parse_namespace(const char *unit, const char *filename, unsigned line, diff --git a/src/netlog/netlog-gperf.gperf b/src/netlog/netlog-gperf.gperf index 17d002b..374bb3d 100644 --- a/src/netlog/netlog-gperf.gperf +++ b/src/netlog/netlog-gperf.gperf @@ -15,12 +15,13 @@ struct ConfigPerfItem; %struct-type %includes %% -Network.Address, config_parse_netlog_remote_address, 0, 0 -Network.Protocol, config_parse_protocol, 0, offsetof(Manager, protocol) -Network.LogFormat, config_parse_log_format, 0, offsetof(Manager, log_format) -Network.Directory, config_parse_string, 0, offsetof(Manager, dir) -Network.Namespace, config_parse_namespace, 0, offsetof(Manager, namespace) -Network.StructuredData, config_parse_string, 0, offsetof(Manager, structured_data) -Network.UseSysLogStructuredData, config_parse_bool, 0, offsetof(Manager, syslog_structured_data) -Network.UseSysLogMsgId, config_parse_bool, 0, offsetof(Manager, syslog_msgid) -Network.ConnectionRetrySec, config_parse_sec, 0, offsetof(Manager, connection_retry_usec) +Network.Address, config_parse_netlog_remote_address, 0, 0 +Network.Protocol, config_parse_protocol, 0, offsetof(Manager, protocol) +Network.LogFormat, config_parse_log_format, 0, offsetof(Manager, log_format) +Network.Directory, config_parse_string, 0, offsetof(Manager, dir) +Network.Namespace, config_parse_namespace, 0, offsetof(Manager, namespace) +Network.StructuredData, config_parse_string, 0, offsetof(Manager, structured_data) +Network.UseSysLogStructuredData, config_parse_bool, 0, offsetof(Manager, syslog_structured_data) +Network.UseSysLogMsgId, config_parse_bool, 0, offsetof(Manager, syslog_msgid) +Network.ConnectionRetrySec, config_parse_sec, 0, offsetof(Manager, connection_retry_usec) +Network.TLSCertificateAuthMode, config_parse_tls_certificate_auth_mode, 0, offsetof(Manager, auth_mode) diff --git a/src/netlog/netlog-manager.c b/src/netlog/netlog-manager.c index 1c3a113..639a951 100644 --- a/src/netlog/netlog-manager.c +++ b/src/netlog/netlog-manager.c @@ -548,6 +548,7 @@ int manager_new(const char *state_file, const char *cursor, Manager **ret) { .state_file = strdup(state_file), .protocol = SYSLOG_TRANSMISSION_PROTOCOL_UDP, .log_format = SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424, + .auth_mode = OPEN_SSL_CERTIFICATE_AUTH_MODE_INVALID, .connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC, .ratelimit = (const RateLimit) { RATELIMIT_INTERVAL_USEC, diff --git a/src/netlog/netlog-manager.h b/src/netlog/netlog-manager.h index 26662bc..f0e44e8 100644 --- a/src/netlog/netlog-manager.h +++ b/src/netlog/netlog-manager.h @@ -68,10 +68,10 @@ struct Manager { SysLogTransmissionProtocol protocol; SysLogTransmissionLogFormat log_format; + OpenSSLCertificateAuthMode auth_mode; bool syslog_structured_data; bool syslog_msgid; - bool encrypt; DTLSManager *dtls; TLSManager *tls; diff --git a/src/netlog/netlog-tls.c b/src/netlog/netlog-tls.c index b21433a..3f24d16 100644 --- a/src/netlog/netlog-tls.c +++ b/src/netlog/netlog-tls.c @@ -12,6 +12,104 @@ #include "io-util.h" #include "iovec-util.h" #include "netlog-tls.h" +#include "string-table.h" + +static const char *const certificate_auth_mode_table[OPEN_SSL_CERTIFICATE_AUTH_MODE_MAX] = { + [OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE] = "no", + [OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW] = "allow", + [OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY] = "deny", + [OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN] = "warn", +}; + +DEFINE_STRING_TABLE_LOOKUP(certificate_auth_mode, int); + +int ssl_verify_certificate_validity(int s, X509_STORE_CTX *store) { + SSL* ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); + _cleanup_(OPENSSL_freep) void *subject = NULL, *issuer = NULL; + TLSManager *m = (TLSManager *) SSL_get_ex_data(ssl, 0); + X509 *cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int error = X509_STORE_CTX_get_error(store); + int verify_mode = SSL_get_verify_mode(ssl); + int r; + + assert(store); + + log_debug("Verifying SSL ceritificates ..."); + + if (cert) { + subject = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + } + + if (verify_mode == SSL_VERIFY_NONE) { + log_debug("SSL Certificate validation DISABLED but Error at depth: %d, issuer = %s, subject = %s: %s", + depth, (char *) subject, (char *) issuer, X509_verify_cert_error_string(error)); + + return 1; + } + + r = SSL_get_verify_result(ssl); + if (r != X509_V_OK) { + switch(r) { + case X509_V_ERR_CERT_HAS_EXPIRED: { + switch (m->auth_mode) { + case OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY: { + log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + return 0; + } + break; + case OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN: { + log_warning_errno(EINVAL, + "Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + + return 1; + } + break; + case OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW: { + log_debug("Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + return 1; + } + + break; + default: + break; + }} + break; + case X509_V_ERR_CERT_REVOKED: { + switch (m->auth_mode) { + case OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY: { + log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + return 0; + } + break; + case OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN: { + log_warning_errno(EINVAL, + "Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + + return 1; + } + break; + case OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW: { + log_debug("Failed to verify certificate: %s", X509_verify_cert_error_string(r)); + return 1; + } + break; + default: + break; + }} + break; + default: + log_debug("Succesffuly validated certificated: %s", X509_verify_cert_error_string(r)); + } + } + + log_debug("SSL ceritificates verified: %s", X509_verify_cert_error_string(r)); + + return 1; +} static int tls_write(TLSManager *m, const char *buf, size_t count) { int r; @@ -103,7 +201,6 @@ int tls_connect(TLSManager *m, SocketAddress *address) { return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "Failed to allocate memory for SSL CTX: %m"); - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); SSL_CTX_set_default_verify_paths(ctx); ssl = SSL_new(ctx); @@ -117,6 +214,17 @@ int tls_connect(TLSManager *m, SocketAddress *address) { "Failed to SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL)); + /* Cerification verification */ + if (m->auth_mode != OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE && m->auth_mode != OPEN_SSL_CERTIFICATE_AUTH_MODE_INVALID) { + log_debug("TLS: enable certificate verification"); + + SSL_set_ex_data(ssl, 0, m); + SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_certificate_validity); + } else { + log_debug("TLS: disable certificate verification"); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } + r = SSL_connect(ssl); if (r <= 0) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), @@ -140,6 +248,7 @@ int tls_connect(TLSManager *m, SocketAddress *address) { log_debug("SSL: Issuer: %s", (char *) issuer); } else log_debug("SSL: No certificates."); + } r = fd_nonblock(fd, true); @@ -181,13 +290,17 @@ void tls_manager_free(TLSManager *m) { free(m); } -int tls_manager_init(TLSManager **ret) { +int tls_manager_init(OpenSSLCertificateAuthMode auth, TLSManager **ret ) { _cleanup_(tls_manager_freep) TLSManager *m = NULL; - m = new0(TLSManager, 1); + m = new(TLSManager, 1); if (!m) return log_oom(); + *m = (TLSManager) { + .auth_mode = auth, + }; + *ret = TAKE_PTR(m); return 0; } diff --git a/src/netlog/netlog-tls.h b/src/netlog/netlog-tls.h index d4fca20..75b59fd 100644 --- a/src/netlog/netlog-tls.h +++ b/src/netlog/netlog-tls.h @@ -8,6 +8,15 @@ #include "socket-util.h" #include "openssl-util.h" +typedef enum OpenSSLCertificateAuthMode { + OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE = 1 << 0, + OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW = 1 << 1, + OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY = 1 << 2, + OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN = 1 << 3, + OPEN_SSL_CERTIFICATE_AUTH_MODE_MAX = 1 << 4, + OPEN_SSL_CERTIFICATE_AUTH_MODE_INVALID = -1, +} OpenSSLCertificateAuthMode; + typedef struct TLSManager TLSManager; struct TLSManager { @@ -18,14 +27,21 @@ struct TLSManager { int fd; bool connected; + OpenSSLCertificateAuthMode auth_mode; }; void tls_manager_free(TLSManager *m); -int tls_manager_init(TLSManager **ret); +int tls_manager_init(OpenSSLCertificateAuthMode auth, TLSManager **ret); int tls_connect(TLSManager *m, SocketAddress *addr); void tls_disconnect(TLSManager *m); int tls_stream_writev(TLSManager *m, const struct iovec *iov, size_t iovcnt); +int ssl_verify_certificate_validity(int status, X509_STORE_CTX *store); + +const char *certificate_auth_mode_to_string(int v) _const_; +int certificate_auth_mode_from_string(const char *s) _pure_; + + DEFINE_TRIVIAL_CLEANUP_FUNC(TLSManager*, tls_manager_free); diff --git a/src/netlog/systemd-netlogd.c b/src/netlog/systemd-netlogd.c index 97fe43d..d17637b 100644 --- a/src/netlog/systemd-netlogd.c +++ b/src/netlog/systemd-netlogd.c @@ -176,7 +176,7 @@ int main(int argc, char **argv) { r = dtls_manager_init(&m->dtls); break; case SYSLOG_TRANSMISSION_PROTOCOL_TLS: - r = tls_manager_init(&m->tls); + r = tls_manager_init( m->auth_mode, &m->tls); break; default: break;