diff --git a/doc/VAULT.md b/doc/VAULT.md index cd20f02c..1cd01d9a 100644 --- a/doc/VAULT.md +++ b/doc/VAULT.md @@ -41,6 +41,10 @@ The available keys and their accepted values are reported in the table below. | log_connections | `off` | Bool | No | Log connects | | log_disconnections | `off` | Bool | No | Log disconnects | | hugepage | `try` | String | No | Huge page support (`off`, `try`, `on`) | +| tls | `off` | Bool | No | Enable Transport Layer Security (TLS) | +| tls_cert_file | | String | No | Certificate file for TLS. This file must be owned by either the user running pgagroal or root. | +| tls_key_file | | String | No | Private key file for TLS. This file must be owned by either the user running pgagroal or root. Additionally permissions must be at least `0640` when owned by root or `0600` otherwise. | +| tls_ca_file | | String | No | Certificate Authority (CA) file for TLS. This file must be owned by either the user running pgagroal or root. | ## [main] diff --git a/doc/man/pgagroal_vault.conf.5.rst b/doc/man/pgagroal_vault.conf.5.rst index a8f3ee06..c0fa67c9 100644 --- a/doc/man/pgagroal_vault.conf.5.rst +++ b/doc/man/pgagroal_vault.conf.5.rst @@ -89,6 +89,18 @@ authentication_timeout hugepage Huge page support. Default is try +tls + Enable Transport Layer Security (TLS). Default is false. Changes require restart in the server section. + +tls_cert_file + Certificate file for TLS. Changes require restart in the server section. + +tls_key_file + Private key file for TLS. Changes require restart in the server section. + +tls_ca_file + Certificate Authority (CA) file for TLS. Changes require restart in the server section. + The options for the main section are host diff --git a/doc/manual/user-12-vault.md b/doc/manual/user-12-vault.md index bccae807..c2490a16 100644 --- a/doc/manual/user-12-vault.md +++ b/doc/manual/user-12-vault.md @@ -43,6 +43,10 @@ The available keys and their accepted values are reported in the table below. | log_disconnections | `off` | Bool | No | Log disconnects | | authentication_timeout | 5 | Int | No | The number of seconds the process will wait for valid credentials | | hugepage | `try` | String | No | Huge page support (`off`, `try`, `on`) | +| tls | `off` | Bool | No | Enable Transport Layer Security (TLS) | +| tls_cert_file | | String | No | Certificate file for TLS. This file must be owned by either the user running pgagroal or root. | +| tls_key_file | | String | No | Private key file for TLS. This file must be owned by either the user running pgagroal or root. Additionally permissions must be at least `0640` when owned by root or `0600` otherwise. | +| tls_ca_file | | String | No | Certificate Authority (CA) file for TLS. This file must be owned by either the user running pgagroal or root. | ## [main] diff --git a/doc/tutorial/06_tls.md b/doc/tutorial/06_tls.md new file mode 100644 index 00000000..2b9f1a2a --- /dev/null +++ b/doc/tutorial/06_tls.md @@ -0,0 +1,122 @@ +## Creating Certificates + +This tutorial will show you how to create self-signed certificate for the server, valid for 365 days, use the following OpenSSL command, replacing `dbhost.yourdomain.com` with the server's host name, here `localhost`: + +``` +openssl req -new -x509 -days 365 -nodes -text -out server.crt \ + -keyout server.key -subj "/CN=dbhost.yourdomain.com" +``` + +then do - + +``` +chmod og-rwx server.key +``` + +because the server will reject the file if its permissions are more liberal than this. For more details on how to create your server private key and certificate, refer to the OpenSSL documentation. + +For the purpose of this tutorial we will assume the client certificate and key same as the server certificate and server key and therefore, these equations always holds - + +`` = `` \ +`` = `` \ +`` = `` \ +`` = `` + +## TLS in `pgagroal` + +This tutorial will show you how to enable tls between `client` and [**pgagroal**](https://github.com/agroal/pgagroal). + +### Preface + +This tutorial assumes that you have already an installation of PostgreSQL 12 (or higher) and [**pgagroal**](https://github.com/agroal/pgagroal). + +In particular, this tutorial refers to the configuration done in [Install pgagroal](https://github.com/pgagroal/pgagroal/blob/master/doc/tutorial/01_install.md). + +### Modify the `pgagroal` configuration + +It is now time to modify the [pgagroal] section of configration file `/etc/pgagroal/pgagroal_vault.conf`, with your editor of choice by adding the following lines in the [pgagroal] section. + +``` +tls = on +tls_cert_file = +tls_key_file = +``` + +#### Only Server Authentication + +If you wish to do only server authentication the aforementioned configuration suffice. + +**Client Request** + +``` +PGSSLMODE=verify-full PGSSLROOTCERT= psql -h localhost -p 2345 -U +``` + + +#### Full Client and Server Authentication + +TO enable server to request for client certificates add another configuration line below the tls + +``` +tls = on +tls_cert_file = +tls_key_file = +tls_ca_file = +``` + +**Client Request** + +``` +PGSSLMODE=verify-full PGSSLCERT= PGSSLKEY= PGSSLROOTCERT= psql -h localhost -p 2345 -U +``` + +## TLS in `pgagroal-vault` + +This tutorial will show you how to enable tls between [**pgagroal-vault**](https://github.com/agroal/pgagroal) and the client (`curl`). + +### Preface + +This tutorial assumes that you have already an installation of PostgreSQL 12 (or higher) and [**pgagroal**](https://github.com/agroal/pgagroal). + +This tutorial aslo assumes that you have a functional [**pgagroal-vault**](https://github.com/agroal/pgagroal) + +In particular, this tutorial refers to the configuration done in [Install pgagroal](https://github.com/pgagroal/pgagroal/blob/master/doc/tutorial/01_install.md) and the configuration done in [Setup pgagroal-vault](https://github.com/pgagroal/pgagroal/blob/master/doc/tutorial/07_vault.md). + +### Modify the `pgagroal-vault` configuration + +It is now time to modify the [pgagroal-vault] section of configration file `/etc/pgagroal/pgagroal_vault.conf`, with your editor of choice by adding the following lines in the [pgagroal-vault] section. + +``` +tls = on +tls_cert_file = +tls_key_file = +``` + +This will add TLS support to the server alongside the standard `http` endpoint, allowing clients to make requests to either the `https` or `http` endpoint. + +#### Only Server Authentication + +If you wish to do only server authentication the aforementioned configuration suffice. + +**Client Request** + +``` +curl --cacert -i https://localhost:2500/users/ +``` + +#### Full Client and Server Authentication + +TO enable server to request for client certificates add another configuration line below the tls + +``` +tls = on +tls_cert_file = +tls_key_file = +tls_ca_file = +``` + +**Client Request** + +``` +curl --cert --key --cacert -i https://localhost:2500/users/ +``` diff --git a/doc/tutorial/06_vault.md b/doc/tutorial/07_vault.md similarity index 100% rename from doc/tutorial/06_vault.md rename to doc/tutorial/07_vault.md diff --git a/src/include/network.h b/src/include/network.h index dcff9927..3615847c 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -169,6 +169,28 @@ pgagroal_socket_is_nonblocking(int fd); int pgagroal_socket_has_error(int fd); +/** + * Read bytes from a socket to buffer + * @param ssl The ssl + * @param fd The descriptor + * @param buffer The buffer to write to + * @param buffer_size Size of buffer + * @return The number of bytes read + */ +int +pgagroal_read_socket(SSL* ssl, int fd, char* buffer, size_t buffer_size); + +/** + * Write bytes from a buffer to socket + * @param ssl The ssl + * @param fd The descriptor + * @param buffer The buffer to write to + * @param buffer_size Size of buffer + * @return The number of bytes written + */ +int +pgagroal_write_socket(SSL* ssl, int fd, char* buffer, size_t buffer_size); + #ifdef __cplusplus } #endif diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index e738ae76..14d2b23d 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -463,6 +463,11 @@ struct configuration atomic_schar log_lock; /**< The logging lock */ char default_log_path[MISC_LENGTH]; /**< The default logging path */ + // TLS support + bool tls; /**< Is TLS enabled */ + char tls_cert_file[MISC_LENGTH]; /**< TLS certificate path */ + char tls_key_file[MISC_LENGTH]; /**< TLS key path */ + char tls_ca_file[MISC_LENGTH]; /**< TLS CA certificate path */ // Prometheus unsigned char hugepage; /**< Huge page support */ int metrics; /**< The metrics port */ @@ -508,11 +513,6 @@ struct main_configuration bool authquery; /**< Is authentication query enabled */ - bool tls; /**< Is TLS enabled */ - char tls_cert_file[MISC_LENGTH]; /**< TLS certificate path */ - char tls_key_file[MISC_LENGTH]; /**< TLS key path */ - char tls_ca_file[MISC_LENGTH]; /**< TLS CA certificate path */ - atomic_ushort active_connections; /**< The active number of connections */ int max_connections; /**< The maximum number of connections */ bool allow_unknown_users; /**< Allow unknown users */ diff --git a/src/include/security.h b/src/include/security.h index 06e8c3b2..1b897a9f 100644 --- a/src/include/security.h +++ b/src/include/security.h @@ -147,6 +147,16 @@ pgagroal_tls_valid(void); int pgagroal_generate_password(int password_length, char** password); +/** + * @brief Accept the SSL connection for the vault from client (curl) + * @param config the vault configuration + * @param client_fd the descriptor + * @param c_ssl the client SSL context + * @return 0 if success, otherwise 1 + */ +int +accept_ssl_vault(struct vault_configuration* config, int client_fd, SSL** c_ssl); + /** * @brief Initialize RNG * diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 90450eab..1d507c23 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -127,7 +127,7 @@ pgagroal_init_configuration(void* shm) } config->failover = false; - config->tls = false; + config->common.tls = false; config->gracefully = false; config->pipeline = PIPELINE_AUTO; config->authquery = false; @@ -575,7 +575,7 @@ pgagroal_validate_configuration(void* shm, bool has_unix_socket, bool has_main_s if (config->pipeline == PIPELINE_AUTO) { - if (config->tls && (strlen(config->tls_cert_file) > 0 || strlen(config->tls_key_file) > 0)) + if (config->common.tls && (strlen(config->common.tls_cert_file) > 0 || strlen(config->common.tls_key_file) > 0)) { tls = true; } @@ -680,7 +680,7 @@ pgagroal_validate_configuration(void* shm, bool has_unix_socket, bool has_main_s } else if (config->pipeline == PIPELINE_PERFORMANCE) { - if (config->tls && (strlen(config->tls_cert_file) > 0 || strlen(config->tls_key_file) > 0)) + if (config->common.tls && (strlen(config->common.tls_cert_file) > 0 || strlen(config->common.tls_key_file) > 0)) { tls = true; } @@ -722,6 +722,7 @@ pgagroal_vault_init_configuration(void* shm) config = (struct vault_configuration*)shm; config->common.port = 0; + config->common.tls = false; config->vault_server.server.port = 0; config->vault_server.server.tls = false; @@ -2616,12 +2617,12 @@ transfer_configuration(struct main_configuration* config, struct main_configurat config->authquery = reload->authquery; - config->tls = reload->tls; - memcpy(config->tls_cert_file, reload->tls_cert_file, MISC_LENGTH); - memcpy(config->tls_key_file, reload->tls_key_file, MISC_LENGTH); - memcpy(config->tls_ca_file, reload->tls_ca_file, MISC_LENGTH); + config->common.tls = reload->common.tls; + memcpy(config->common.tls_cert_file, reload->common.tls_cert_file, MISC_LENGTH); + memcpy(config->common.tls_key_file, reload->common.tls_key_file, MISC_LENGTH); + memcpy(config->common.tls_ca_file, reload->common.tls_ca_file, MISC_LENGTH); - if (config->tls && (config->pipeline == PIPELINE_SESSION || config->pipeline == PIPELINE_TRANSACTION)) + if (config->common.tls && (config->pipeline == PIPELINE_SESSION || config->pipeline == PIPELINE_TRANSACTION)) { if (pgagroal_tls_valid()) { @@ -3589,7 +3590,7 @@ pgagroal_write_config_value(char* buffer, char* config_key, size_t buffer_size) } else if (!strncmp(key, "tls", MISC_LENGTH)) { - return to_bool(buffer, config->tls); + return to_bool(buffer, config->common.tls); } else if (!strncmp(key, "auth_query", MISC_LENGTH)) { @@ -3597,15 +3598,15 @@ pgagroal_write_config_value(char* buffer, char* config_key, size_t buffer_size) } else if (!strncmp(key, "tls_ca_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_ca_file, buffer_size); + return to_string(buffer, config->common.tls_ca_file, buffer_size); } else if (!strncmp(key, "tls_cert_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_cert_file, buffer_size); + return to_string(buffer, config->common.tls_cert_file, buffer_size); } else if (!strncmp(key, "tls_key_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_key_file, buffer_size); + return to_string(buffer, config->common.tls_key_file, buffer_size); } else if (!strncmp(key, "blocking_timeout", MISC_LENGTH)) { @@ -4397,7 +4398,7 @@ pgagroal_apply_main_configuration(struct main_configuration* config, } else if (key_in_section("tls", section, key, true, &unknown)) { - if (as_bool(value, &config->tls)) + if (as_bool(value, &config->common.tls)) { unknown = true; } @@ -4416,7 +4417,7 @@ pgagroal_apply_main_configuration(struct main_configuration* config, { max = MISC_LENGTH - 1; } - memcpy(config->tls_ca_file, value, max); + memcpy(config->common.tls_ca_file, value, max); } else if (key_in_section("tls_ca_file", section, key, false, &unknown)) { @@ -4434,7 +4435,7 @@ pgagroal_apply_main_configuration(struct main_configuration* config, { max = MISC_LENGTH - 1; } - memcpy(config->tls_cert_file, value, max); + memcpy(config->common.tls_cert_file, value, max); } else if (key_in_section("tls_cert_file", section, key, false, &unknown)) { @@ -4452,7 +4453,7 @@ pgagroal_apply_main_configuration(struct main_configuration* config, { max = MISC_LENGTH - 1; } - memcpy(config->tls_key_file, value, max); + memcpy(config->common.tls_key_file, value, max); } else if (key_in_section("tls_key_file", section, key, false, &unknown)) { @@ -4779,60 +4780,60 @@ pgagroal_apply_vault_configuration(struct vault_configuration* config, } memcpy(&srv->user.username, value, max); } - else if (key_in_section("metrics", section, key, true, &unknown)) + else if (key_in_section("tls", section, key, true, &unknown)) { - if (as_int(value, &config->common.metrics)) + if (as_bool(value, &config->common.tls)) { unknown = true; } } - else if (key_in_section("metrics_cache_max_age", section, key, true, &unknown)) + else if (key_in_section("tls_ca_file", section, key, true, &unknown)) { - if (as_seconds(value, &config->common.metrics_cache_max_age, PGAGROAL_PROMETHEUS_CACHE_DISABLED)) + max = strlen(value); + if (max > MISC_LENGTH - 1) { - unknown = true; + max = MISC_LENGTH - 1; } + memcpy(config->common.tls_ca_file, value, max); } - else if (key_in_section("metrics_cache_max_size", section, key, true, &unknown)) + else if (key_in_section("tls_cert_file", section, key, true, &unknown)) { - if (as_bytes(value, &config->common.metrics_cache_max_size, PROMETHEUS_DEFAULT_CACHE_SIZE)) + max = strlen(value); + if (max > MISC_LENGTH - 1) { - unknown = true; + max = MISC_LENGTH - 1; } + memcpy(config->common.tls_cert_file, value, max); } - else if (key_in_section("tls", section, key, false, &unknown)) + else if (key_in_section("tls_key_file", section, key, true, &unknown)) { - if (as_bool(value, &srv->server.tls)) + max = strlen(value); + if (max > MISC_LENGTH - 1) { - unknown = true; + max = MISC_LENGTH - 1; } + memcpy(config->common.tls_key_file, value, max); } - else if (key_in_section("tls_ca_file", section, key, false, &unknown)) + else if (key_in_section("metrics", section, key, true, &unknown)) { - max = strlen(value); - if (max > MISC_LENGTH - 1) + if (as_int(value, &config->common.metrics)) { - max = MISC_LENGTH - 1; + unknown = true; } - memcpy(&srv->server.tls_ca_file, value, max); } - else if (key_in_section("tls_cert_file", section, key, false, &unknown)) + else if (key_in_section("metrics_cache_max_age", section, key, true, &unknown)) { - max = strlen(value); - if (max > MISC_LENGTH - 1) + if (as_seconds(value, &config->common.metrics_cache_max_age, PGAGROAL_PROMETHEUS_CACHE_DISABLED)) { - max = MISC_LENGTH - 1; + unknown = true; } - memcpy(&srv->server.tls_cert_file, value, max); } - else if (key_in_section("tls_key_file", section, key, false, &unknown)) + else if (key_in_section("metrics_cache_max_size", section, key, true, &unknown)) { - max = strlen(value); - if (max > MISC_LENGTH - 1) + if (as_bytes(value, &config->common.metrics_cache_max_size, PROMETHEUS_DEFAULT_CACHE_SIZE)) { - max = MISC_LENGTH - 1; + unknown = true; } - memcpy(&srv->server.tls_key_file, value, max); } else if (key_in_section("authentication_timeout", section, key, true, &unknown)) { diff --git a/src/libpgagroal/network.c b/src/libpgagroal/network.c index 21bc258e..bdec0b3e 100644 --- a/src/libpgagroal/network.c +++ b/src/libpgagroal/network.c @@ -543,6 +543,38 @@ pgagroal_socket_buffers(int fd, int* buffer_size) return 0; } +int +pgagroal_read_socket(SSL* ssl, int fd, char* buffer, size_t buffer_size) +{ + int byte_read; + if (ssl) + { + byte_read = SSL_read(ssl, buffer, buffer_size); + } + else + { + byte_read = read(fd, buffer, buffer_size); + } + + return byte_read; +} + +int +pgagroal_write_socket(SSL* ssl, int fd, char* buffer, size_t buffer_size) +{ + int byte_write; + if (ssl) + { + byte_write = SSL_write(ssl, buffer, buffer_size); + } + else + { + byte_write = write(fd, buffer, buffer_size); + } + + return byte_write; +} + /** * */ diff --git a/src/libpgagroal/prometheus.c b/src/libpgagroal/prometheus.c index 9028d199..49ed797c 100644 --- a/src/libpgagroal/prometheus.c +++ b/src/libpgagroal/prometheus.c @@ -348,7 +348,7 @@ pgagroal_vault_init_prometheus(size_t* p_size, void** p_shmem) error: - return 1; + return 1; } void diff --git a/src/libpgagroal/security.c b/src/libpgagroal/security.c index 9f9bb21c..a7dad428 100644 --- a/src/libpgagroal/security.c +++ b/src/libpgagroal/security.c @@ -238,7 +238,7 @@ pgagroal_authenticate(int client_fd, char* address, int* slot, SSL** client_ssl, { pgagroal_log_debug("SSL request from client: %d", client_fd); - if (config->tls) + if (config->common.tls) { SSL_CTX* ctx = NULL; @@ -469,6 +469,56 @@ pgagroal_authenticate(int client_fd, char* address, int* slot, SSL** client_ssl, return AUTH_ERROR; } +int +accept_ssl_vault(struct vault_configuration* config, int client_fd, SSL** client_ssl) +{ + + pgagroal_log_debug("SSL request from client: %d", client_fd); + int status = MESSAGE_STATUS_ERROR; + SSL_CTX* ctx = NULL; + SSL* c_ssl = NULL; + + /* We are acting as a server against the client */ + if (create_ssl_ctx(false, &ctx)) + { + goto error; + } + + if (create_ssl_server(ctx, client_fd, &c_ssl)) + { + goto error; + } + + *client_ssl = c_ssl; + + /* Switch to TLS mode */ + status = SSL_accept(c_ssl); + if (status != 1) + { + unsigned long err; + + err = ERR_get_error(); + pgagroal_log_error("SSL failed: %s", ERR_reason_error_string(err)); + goto error; + } + + return 0; +error: + + if (ctx != NULL) + { + SSL_CTX_free(ctx); + } + + if (c_ssl != NULL) + { + SSL_free(c_ssl); + } + + pgagroal_log_debug("accept_ssl_vault: ERROR"); + return 1; +} + int pgagroal_prefill_auth(char* username, char* password, char* database, int* slot, SSL** server_ssl) { @@ -603,7 +653,7 @@ pgagroal_remote_management_auth(int client_fd, char* address, SSL** client_ssl) { pgagroal_log_debug("SSL request from client: %d", client_fd); - if (config->tls) + if (config->common.tls) { SSL_CTX* ctx = NULL; @@ -3337,49 +3387,49 @@ pgagroal_tls_valid(void) config = (struct main_configuration*)shmem; - if (config->tls) + if (config->common.tls) { - if (strlen(config->tls_cert_file) == 0) + if (strlen(config->common.tls_cert_file) == 0) { pgagroal_log_error("No TLS certificate defined"); goto error; } - if (strlen(config->tls_key_file) == 0) + if (strlen(config->common.tls_key_file) == 0) { pgagroal_log_error("No TLS private key defined"); goto error; } - if (stat(config->tls_cert_file, &st) == -1) + if (stat(config->common.tls_cert_file, &st) == -1) { - pgagroal_log_error("Can't locate TLS certificate file: %s", config->tls_cert_file); + pgagroal_log_error("Can't locate TLS certificate file: %s", config->common.tls_cert_file); goto error; } if (!S_ISREG(st.st_mode)) { - pgagroal_log_error("TLS certificate file is not a regular file: %s", config->tls_cert_file); + pgagroal_log_error("TLS certificate file is not a regular file: %s", config->common.tls_cert_file); goto error; } if (st.st_uid && st.st_uid != geteuid()) { - pgagroal_log_error("TLS certificate file not owned by user or root: %s", config->tls_cert_file); + pgagroal_log_error("TLS certificate file not owned by user or root: %s", config->common.tls_cert_file); goto error; } memset(&st, 0, sizeof(struct stat)); - if (stat(config->tls_key_file, &st) == -1) + if (stat(config->common.tls_key_file, &st) == -1) { - pgagroal_log_error("Can't locate TLS private key file: %s", config->tls_key_file); + pgagroal_log_error("Can't locate TLS private key file: %s", config->common.tls_key_file); goto error; } if (!S_ISREG(st.st_mode)) { - pgagroal_log_error("TLS private key file is not a regular file: %s", config->tls_key_file); + pgagroal_log_error("TLS private key file is not a regular file: %s", config->common.tls_key_file); goto error; } @@ -3387,7 +3437,7 @@ pgagroal_tls_valid(void) { if (st.st_mode & (S_IRWXG | S_IRWXO)) { - pgagroal_log_error("TLS private key file must have 0600 permissions when owned by a non-root user: %s", config->tls_key_file); + pgagroal_log_error("TLS private key file must have 0600 permissions when owned by a non-root user: %s", config->common.tls_key_file); goto error; } } @@ -3395,36 +3445,36 @@ pgagroal_tls_valid(void) { if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { - pgagroal_log_error("TLS private key file must have at least 0640 permissions when owned by root: %s", config->tls_key_file); + pgagroal_log_error("TLS private key file must have at least 0640 permissions when owned by root: %s", config->common.tls_key_file); goto error; } } else { - pgagroal_log_error("TLS private key file not owned by user or root: %s", config->tls_key_file); + pgagroal_log_error("TLS private key file not owned by user or root: %s", config->common.tls_key_file); goto error; } - if (strlen(config->tls_ca_file) > 0) + if (strlen(config->common.tls_ca_file) > 0) { memset(&st, 0, sizeof(struct stat)); - if (stat(config->tls_ca_file, &st) == -1) + if (stat(config->common.tls_ca_file, &st) == -1) { - pgagroal_log_error("Can't locate TLS CA file: %s", config->tls_ca_file); + pgagroal_log_error("Can't locate TLS CA file: %s", config->common.tls_ca_file); goto error; } if (!S_ISREG(st.st_mode)) { - pgagroal_log_error("TLS CA file is not a regular file: %s", config->tls_ca_file); + pgagroal_log_error("TLS CA file is not a regular file: %s", config->common.tls_ca_file); goto error; } if (st.st_uid && st.st_uid != geteuid()) { - pgagroal_log_error("TLS CA file not owned by user or root: %s", config->tls_ca_file); + pgagroal_log_error("TLS CA file not owned by user or root: %s", config->common.tls_ca_file); goto error; } } @@ -4533,9 +4583,9 @@ create_ssl_server(SSL_CTX* ctx, int socket, SSL** ssl) { SSL* s = NULL; STACK_OF(X509_NAME) * root_cert_list = NULL; - struct main_configuration* config; + struct configuration* config; - config = (struct main_configuration*)shmem; + config = (struct configuration*)shmem; if (strlen(config->tls_cert_file) == 0) { diff --git a/src/vault.c b/src/vault.c index 69636973..4c8313de 100644 --- a/src/vault.c +++ b/src/vault.c @@ -58,6 +58,11 @@ #include #endif +#define CLIENTSSL_ON_SERVERSSL_ON 0 +#define CLIENTSSL_ON_SERVERSSL_OFF 1 +#define CLIENTSSL_OFF_SERVERSSL_ON 2 +#define CLIENTSSL_OFF_SERVERSSL_OFF 3 + #define MAX_FDS 64 static void accept_vault_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); @@ -68,8 +73,10 @@ static int connect_pgagroal(struct vault_configuration* config, char* username, static void route_users(char* username, char** response, SSL* s_ssl, int client_fd); static void route_not_found(char** response); static void route_found(char** response, char* password); -static int router(SSL* ssl, int client_fd); -static void shutdown_ports(void); +static void route_redirect(char** response, char* redirect_link); +static int router(SSL* ccl, SSL* ssl, int client_fd); +static bool is_ssl_request(int client_fd); +static int get_connection_state(struct vault_configuration* config, int client_fd); static volatile int keep_running = 1; static char** argv_ptr; @@ -83,32 +90,55 @@ static int server_fds_length = -1; static int default_buffer_size = DEFAULT_BUFFER_SIZE; static int -router(SSL* s_ssl, int client_fd) +router(SSL* c_ssl, SSL* s_ssl, int client_fd) { int exit_code = 0; - ssize_t bytes_read; + int connection_state; ssize_t bytes_write; - char* body = NULL; + struct vault_configuration* config; char* response = NULL; char method[8]; char path[128]; char buffer[HTTP_BUFFER_SIZE]; - char contents[HTTP_BUFFER_SIZE]; char username[MAX_USERNAME_LENGTH + 1]; // Assuming username is less than 128 characters + char* redirect_link = NULL; + config = (struct vault_configuration*)shmem; memset(&response, 0, sizeof(response)); + memset(&buffer, 0, sizeof(buffer)); - // Read the request - bytes_read = read(client_fd, buffer, sizeof(buffer) - 1); - buffer[bytes_read] = '\0'; - - sscanf(buffer, "%7s %127s", method, path); - - // Extract the POST data - body = strstr(buffer, "\r\n\r\n"); - if (body) + connection_state = get_connection_state(config, client_fd); + switch (connection_state) { - strcpy(contents, body + 4); + case CLIENTSSL_ON_SERVERSSL_ON: + if (accept_ssl_vault(config, client_fd, &c_ssl)) + { + pgagroal_log_error("accept_ssl_vault: SSL connection failed"); + exit_code = 1; + goto exit; + } + pgagroal_read_socket(c_ssl, client_fd, buffer, sizeof(buffer)); + sscanf(buffer, "%7s %127s", method, path); + break; + case CLIENTSSL_OFF_SERVERSSL_ON: + pgagroal_read_socket(c_ssl, client_fd, buffer, sizeof(buffer)); + sscanf(buffer, "%7s %127s", method, path); + redirect_link = pgagroal_append(redirect_link, "https://"); + redirect_link = pgagroal_append(redirect_link, config->common.host); + redirect_link = pgagroal_append(redirect_link, ":"); + redirect_link = pgagroal_append_int(redirect_link, config->common.port); + redirect_link = pgagroal_append(redirect_link, path); + route_redirect(&response, redirect_link); + pgagroal_log_error("client must initiate tls handshake"); + goto send; + case CLIENTSSL_OFF_SERVERSSL_OFF: + pgagroal_read_socket(c_ssl, client_fd, buffer, sizeof(buffer)); + sscanf(buffer, "%7s %127s", method, path); + break; + case CLIENTSSL_ON_SERVERSSL_OFF: + pgagroal_log_error("client requests tls connection to http server"); + default: + return 1; } // Parse URL parameters for GET requests only @@ -132,17 +162,16 @@ router(SSL* s_ssl, int client_fd) route_not_found(&response); } +send: // Send the response - bytes_write = write(client_fd, response, strlen(response)); - + bytes_write = pgagroal_write_socket(c_ssl, client_fd, response, strlen(response)); if (bytes_write <= 0) { exit_code = 1; } - pgagroal_prometheus_client_sockets_sub(); free(response); - +exit: return exit_code; } @@ -207,6 +236,18 @@ route_found(char** response, char* password) *response = tmp_response; } +static void +route_redirect(char** response, char* redirect_link) +{ + char* tmp_response = NULL; + tmp_response = pgagroal_append(tmp_response, "HTTP/1.1 301 Moved Permanently\r\n"); + tmp_response = pgagroal_append(tmp_response, "Content-Length: 0\r\n"); + tmp_response = pgagroal_append(tmp_response, "Location: "); + tmp_response = pgagroal_append(tmp_response, redirect_link); + tmp_response = pgagroal_append(tmp_response, "\r\n"); + *response = tmp_response; +} + static int connect_pgagroal(struct vault_configuration* config, char* username, char* password, SSL** s_ssl, int* client_socket) { @@ -244,6 +285,56 @@ connect_pgagroal(struct vault_configuration* config, char* username, char* passw return 0; } +static bool +is_ssl_request(int client_fd) +{ + ssize_t peek_bytes; + char peek_buffer[HTTP_BUFFER_SIZE]; + bool ssl_req = false; + + // MSG_Peek + peek_bytes = recv(client_fd, peek_buffer, sizeof(peek_buffer), MSG_PEEK); + if (peek_bytes <= 0) + { + pgagroal_log_error("unable to peek network data from client"); + close(client_fd); + exit(1); + } + + // Check for SSL request by matching `Client Hello` bytes + if ( + ((unsigned char)peek_buffer[0] == 0x16) && + ((unsigned char)peek_buffer[1] == 0x03) && + ((unsigned char)peek_buffer[2] == 0x01 || (unsigned char)peek_buffer[2] == 0x02 || (unsigned char)peek_buffer[2] == 0x03 || (unsigned char)peek_buffer[2] == 0x04) + ) + { + ssl_req = true; + } + + return ssl_req; +} + +static int +get_connection_state(struct vault_configuration* config, int client_fd) +{ + if (config->common.tls) + { + if (is_ssl_request(client_fd)) + { + return CLIENTSSL_ON_SERVERSSL_ON; + } + else + { + return CLIENTSSL_OFF_SERVERSSL_ON; + } + } + else if (is_ssl_request(client_fd)) + { + return CLIENTSSL_ON_SERVERSSL_OFF; + } + return CLIENTSSL_OFF_SERVERSSL_OFF; +} + static void start_vault_io(void) { @@ -607,6 +698,7 @@ accept_vault_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) int client_fd; char address[INET6_ADDRSTRLEN]; pid_t pid; + SSL* c_ssl = NULL; SSL* s_ssl = NULL; struct vault_configuration* config; @@ -681,9 +773,11 @@ accept_vault_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) ev_loop_fork(loop); shutdown_ports(); - if (router(s_ssl, client_fd)) + // Handle http request + if (router(c_ssl, s_ssl, client_fd)) { pgagroal_log_error("Couldn't write to client"); + pgagroal_disconnect(client_fd); exit(1); }