diff --git a/examples/sudo_logsrvd.conf b/examples/sudo_logsrvd.conf index cc8ebeab8..8a9baad5b 100644 --- a/examples/sudo_logsrvd.conf +++ b/examples/sudo_logsrvd.conf @@ -28,16 +28,26 @@ # By default, server connections are not encrypted. #tls = true +# If set, server certificate will be verified at server startup and +# also connecting clients will perform server authentication by +# verifying the server's certificate and identity. +#tls_verify = true + +# Whether to verify client certificates for TLS connections. +# By default client certs are not checked. +#tls_checkpeer = false + # Path to the certificate authority bundle file in PEM format. +# Required if 'tls_verify' or 'tls_checkpeer' is set. #tls_cacert = /etc/ssl/sudo/cacert.pem # Path to the server's certificate file in PEM format. # Required for TLS connections. #tls_cert = /etc/ssl/sudo/logsrvd_cert.pem -# Whether to verify client certificates for TLS connections. -# By default client certs are not checked. -#tls_checkpeer = false +# Path to the server's private key file in PEM format. +# Required for TLS connections. +#tls_key = /etc/ssl/sudo/private/logsrvd_key.pem # TLS cipher list (see "CIPHER LIST FORMAT" in the openssl-ciphers manual). # NOTE that this setting is only effective if the negotiated protocol @@ -53,10 +63,6 @@ # If not set, the server will use the OpenSSL defaults. #tls_dhparams = /etc/ssl/sudo/logsrvd_dhparams.pem -# Path to the server's private key file in PEM format. -# Required for TLS connections. -#tls_key = /etc/ssl/sudo/private/logsrvd_key.pem - [iolog] # The top-level directory to use when constructing the path name for the # I/O log directory. The session sequence number, if any, is stored here. diff --git a/include/log_server.pb-c.h b/include/log_server.pb-c.h index c931ed3c8..6ae81ea81 100644 --- a/include/log_server.pb-c.h +++ b/include/log_server.pb-c.h @@ -409,6 +409,10 @@ struct _ServerHello * true if server uses tls protocol */ protobuf_c_boolean tls; + /* + * true if server auth has to be performed + */ + protobuf_c_boolean tls_server_auth; /* * true if client auth is required with signed cert */ @@ -416,7 +420,7 @@ struct _ServerHello }; #define SERVER_HELLO__INIT \ { PROTOBUF_C_MESSAGE_INIT (&server_hello__descriptor) \ - , (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string, 0,NULL, 0, 0 } + , (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string, 0,NULL, 0, 0, 0 } /* ClientMessage methods */ diff --git a/lib/logsrv/log_server.pb-c.c b/lib/logsrv/log_server.pb-c.c index 94cc0462e..ac54f2e4c 100644 --- a/lib/logsrv/log_server.pb-c.c +++ b/lib/logsrv/log_server.pb-c.c @@ -1578,7 +1578,7 @@ const ProtobufCMessageDescriptor server_message__descriptor = (ProtobufCMessageInit) server_message__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor server_hello__field_descriptors[5] = +static const ProtobufCFieldDescriptor server_hello__field_descriptors[6] = { { "server_id", @@ -1629,11 +1629,23 @@ static const ProtobufCFieldDescriptor server_hello__field_descriptors[5] = 0,NULL,NULL /* reserved1,reserved2, etc */ }, { - "tls_reqcert", + "tls_server_auth", 5, PROTOBUF_C_LABEL_NONE, PROTOBUF_C_TYPE_BOOL, 0, /* quantifier_offset */ + offsetof(ServerHello, tls_server_auth), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "tls_reqcert", + 6, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ offsetof(ServerHello, tls_reqcert), NULL, NULL, @@ -1646,12 +1658,13 @@ static const unsigned server_hello__field_indices_by_name[] = { 0, /* field[0] = server_id */ 2, /* field[2] = servers */ 3, /* field[3] = tls */ - 4, /* field[4] = tls_reqcert */ + 5, /* field[5] = tls_reqcert */ + 4, /* field[4] = tls_server_auth */ }; static const ProtobufCIntRange server_hello__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 5 } + { 0, 6 } }; const ProtobufCMessageDescriptor server_hello__descriptor = { @@ -1661,7 +1674,7 @@ const ProtobufCMessageDescriptor server_hello__descriptor = "ServerHello", "", sizeof(ServerHello), - 5, + 6, server_hello__field_descriptors, server_hello__field_indices_by_name, 1, server_hello__number_ranges, diff --git a/lib/logsrv/log_server.proto b/lib/logsrv/log_server.proto index da10fb153..b83953966 100644 --- a/lib/logsrv/log_server.proto +++ b/lib/logsrv/log_server.proto @@ -126,5 +126,6 @@ message ServerHello { string redirect = 2; /* optional redirect if busy */ repeated string servers = 3; /* optional list of known servers */ bool tls = 4; /* true if server uses tls protocol */ - bool tls_reqcert = 5; /* true if client auth is required with signed cert */ + bool tls_server_auth = 5; /* true if server auth has to be performed */ + bool tls_reqcert = 6; /* true if client auth is required with signed cert */ } diff --git a/logsrvd/logsrvd.c b/logsrvd/logsrvd.c index 6cfa91c0d..aab6e8c94 100644 --- a/logsrvd/logsrvd.c +++ b/logsrvd/logsrvd.c @@ -178,9 +178,11 @@ fmt_hello_message(struct connection_buffer *buf) hello.server_id = (char *)server_id; #if defined(HAVE_OPENSSL) hello.tls = logsrvd_conf_get_tls_opt(); + hello.tls_server_auth = logsrvd_get_tls_config()->verify; hello.tls_reqcert = logsrvd_get_tls_config()->check_peer; #else hello.tls = false; + hello.tls_server_auth = false; hello.tls_reqcert = false; #endif msg.hello = &hello; @@ -970,9 +972,11 @@ verify_peer_identity(int preverify_ok, X509_STORE_CTX *ctx) switch(result) { case MatchFound: - debug_return_int(1); + debug_return_int(1); default: - debug_return_int(0); + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "hostname validation failed"); + debug_return_int(0); } } @@ -1101,6 +1105,7 @@ init_tls_server_context(void) const SSL_METHOD *method; SSL_CTX *ctx = NULL; const struct logsrvd_tls_config *tls_config = logsrvd_get_tls_config(); + bool ca_bundle_required = tls_config->verify | tls_config->check_peer; debug_decl(init_tls_server_context, SUDO_DEBUG_UTIL); SSL_library_init(); @@ -1127,29 +1132,39 @@ init_tls_server_context(void) goto bad; } - if (tls_config->cacert_path != NULL) { - STACK_OF(X509_NAME) *cacerts = - SSL_load_client_CA_file(tls_config->cacert_path); - if (cacerts == NULL) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "calling SSL_load_client_CA_file() failed: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto bad; - } else { - SSL_CTX_set_client_CA_list(ctx, cacerts); - - /* set the location of the CA bundle file for verification */ - if (SSL_CTX_load_verify_locations(ctx, tls_config->cacert_path, NULL) <= 0) { + /* if server or client authentication is required, CA bundle file has to be prepared */ + if (ca_bundle_required) { + if (tls_config->cacert_path != NULL) { + STACK_OF(X509_NAME) *cacerts = + SSL_load_client_CA_file(tls_config->cacert_path); + if (cacerts == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "calling SSL_CTX_load_verify_locations() failed: %s", + "calling SSL_load_client_CA_file() failed: %s", ERR_error_string(ERR_get_error(), NULL)); goto bad; + } else { + SSL_CTX_set_client_CA_list(ctx, cacerts); + + /* set the location of the CA bundle file for verification */ + if (SSL_CTX_load_verify_locations(ctx, tls_config->cacert_path, NULL) <= 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "calling SSL_CTX_load_verify_locations() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto bad; + } } } - } - if (!verify_server_cert(ctx, tls_config)) { - goto bad; + /* only verify server cert if it is set in the configuration */ + if (tls_config->verify) { + + if (!verify_server_cert(ctx, tls_config)) { + goto bad; + } + } else { + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "skipping server cert check"); + } } /* if peer authentication is enabled, verify client cert during TLS handshake @@ -1303,6 +1318,11 @@ tls_handshake_cb(int fd, int what, void *v) sudo_warn(U_("unable to add event to queue")); } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "TLS version: %s, negotiated cipher suite: %s", + SSL_get_version(closure->ssl), + SSL_get_cipher(closure->ssl)); + debug_return; bad: connection_closure_free(closure); diff --git a/logsrvd/logsrvd.h b/logsrvd/logsrvd.h index ef5ab687d..7fc52e2d7 100644 --- a/logsrvd/logsrvd.h +++ b/logsrvd/logsrvd.h @@ -136,6 +136,7 @@ struct logsrvd_tls_config { char *dhparams_path; char *ciphers_v12; char *ciphers_v13; + bool verify; bool check_peer; }; diff --git a/logsrvd/logsrvd_conf.c b/logsrvd/logsrvd_conf.c index e9fb12618..e71edf856 100644 --- a/logsrvd/logsrvd_conf.c +++ b/logsrvd/logsrvd_conf.c @@ -526,6 +526,19 @@ cb_tls_ciphers13(struct logsrvd_config *config, const char *str) debug_return_bool(true); } +static bool +cb_tls_verify(struct logsrvd_config *config, const char *str) +{ + int val; + debug_decl(cb_tls_verify, SUDO_DEBUG_UTIL); + + if ((val = sudo_strtobool(str)) == -1) + debug_return_bool(false); + + config->server.tls_config.verify = val; + debug_return_bool(true); +} + static bool cb_tls_checkpeer(struct logsrvd_config *config, const char *str) { @@ -706,6 +719,7 @@ static struct logsrvd_config_entry server_conf_entries[] = { { "tls_ciphers_v12", cb_tls_ciphers12 }, { "tls_ciphers_v13", cb_tls_ciphers13 }, { "tls_checkpeer", cb_tls_checkpeer }, + { "tls_verify", cb_tls_verify }, #endif { NULL } }; @@ -882,6 +896,8 @@ logsrvd_conf_alloc(void) #if defined(HAVE_OPENSSL) config->server.tls_config.cacert_path = strdup(DEFAULT_CA_CERT_PATH); config->server.tls_config.cert_path = strdup(DEFAULT_SERVER_CERT_PATH); + config->server.tls_config.verify = true; + config->server.tls_config.check_peer = false; #endif /* I/O log defaults */ diff --git a/plugins/sudoers/iolog_client.c b/plugins/sudoers/iolog_client.c index aca72accb..69ed53db3 100644 --- a/plugins/sudoers/iolog_client.c +++ b/plugins/sudoers/iolog_client.c @@ -281,15 +281,10 @@ verify_peer_identity(int preverify_ok, X509_STORE_CTX *ctx) } static bool -tls_init(struct client_closure *closure, bool cert_required) +tls_init(struct client_closure *closure, bool verify, bool cert_required) { debug_decl(tls_init, SUDOERS_DEBUG_PLUGIN); - if (closure->log_details->ca_bundle == NULL) { - sudo_warnx(U_("CA bundle file is not set in sudoers")); - goto bad; - } - SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); @@ -312,18 +307,27 @@ tls_init(struct client_closure *closure, bool cert_required) SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1); #endif - /* sets the location of the CA bundle file for verification purposes */ - if (SSL_CTX_load_verify_locations(closure->ssl_ctx, - closure->log_details->ca_bundle, NULL) <= 0) { - sudo_warnx(U_("Calling SSL_CTX_load_verify_locations() failed: %s"), - ERR_error_string(ERR_get_error(), NULL)); - goto bad; - } - /* turn on server cert verification during the handshake. - hostname matching will be done in a custom callback (verify_peer_identity). + /* if server explicitly requests it, turn on server cert verification + during the handshake. Hostname matching will be done in a custom + callback (verify_peer_identity). */ - SSL_CTX_set_verify(closure->ssl_ctx, SSL_VERIFY_PEER, verify_peer_identity); + if (verify) { + if (closure->log_details->ca_bundle == NULL) { + sudo_warnx(U_("CA bundle file is not set in sudoers")); + goto bad; + } + + /* sets the location of the CA bundle file for verification purposes */ + if (SSL_CTX_load_verify_locations(closure->ssl_ctx, + closure->log_details->ca_bundle, NULL) <= 0) { + sudo_warnx(U_("Calling SSL_CTX_load_verify_locations() failed: %s"), + ERR_error_string(ERR_get_error(), NULL)); + goto bad; + } + + SSL_CTX_set_verify(closure->ssl_ctx, SSL_VERIFY_PEER, verify_peer_identity); + } /* if the server requests client authentication with signed certificate */ if (cert_required) { @@ -1057,7 +1061,7 @@ handle_server_hello(ServerHello *msg, struct client_closure *closure) #if defined(HAVE_OPENSSL) /* if server requested TLS */ if (msg->tls) { - if (!tls_init(closure, msg->tls_reqcert)) { + if (!tls_init(closure, msg->tls_server_auth, msg->tls_reqcert)) { sudo_warnx(U_("TLS initialization was unsuccessful")); debug_return_bool(false); }