|
Packit |
4e8bc4 |
#include "memcached.h"
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#ifdef TLS
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#include "tls.h"
|
|
Packit |
4e8bc4 |
#include <string.h>
|
|
Packit |
4e8bc4 |
#include <sysexits.h>
|
|
Packit |
4e8bc4 |
#include <sys/param.h>
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#ifndef MAXPATHLEN
|
|
Packit |
4e8bc4 |
#define MAXPATHLEN 4096
|
|
Packit |
4e8bc4 |
#endif
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static pthread_mutex_t ssl_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
const unsigned MAX_ERROR_MSG_SIZE = 128;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
void SSL_LOCK() {
|
|
Packit |
4e8bc4 |
pthread_mutex_lock(&(ssl_ctx_lock));
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
void SSL_UNLOCK(void) {
|
|
Packit |
4e8bc4 |
pthread_mutex_unlock(&(ssl_ctx_lock));
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* Reads decrypted data from the underlying BIO read buffers,
|
|
Packit |
4e8bc4 |
* which reads from the socket.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
ssize_t ssl_read(conn *c, void *buf, size_t count) {
|
|
Packit |
4e8bc4 |
assert (c != NULL);
|
|
Packit |
4e8bc4 |
/* TODO : document the state machine interactions for SSL_read with
|
|
Packit |
4e8bc4 |
non-blocking sockets/ SSL re-negotiations
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
return SSL_read(c->ssl, buf, count);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* SSL sendmsg implementation. Perform a SSL_write.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
ssize_t ssl_sendmsg(conn *c, struct msghdr *msg, int flags) {
|
|
Packit |
4e8bc4 |
assert (c != NULL);
|
|
Packit |
4e8bc4 |
size_t buf_remain = settings.ssl_wbuf_size;
|
|
Packit |
4e8bc4 |
size_t bytes = 0;
|
|
Packit |
4e8bc4 |
size_t to_copy;
|
|
Packit |
4e8bc4 |
int i;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// ssl_wbuf is pointing to the buffer allocated in the worker thread.
|
|
Packit |
4e8bc4 |
assert(c->ssl_wbuf);
|
|
Packit |
4e8bc4 |
// TODO: allocate a fix buffer in crawler/logger if they start using
|
|
Packit |
4e8bc4 |
// the sendmsg method. Also, set c->ssl_wbuf when the side thread
|
|
Packit |
4e8bc4 |
// start owning the connection and reset the pointer in
|
|
Packit |
4e8bc4 |
// conn_worker_readd.
|
|
Packit |
4e8bc4 |
// Currntly this connection would not be served by a different thread
|
|
Packit |
4e8bc4 |
// than the one it's assigned.
|
|
Packit |
4e8bc4 |
assert(pthread_equal(c->thread->thread_id, pthread_self()) != 0);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
char *bp = c->ssl_wbuf;
|
|
Packit |
4e8bc4 |
for (i = 0; i < msg->msg_iovlen; i++) {
|
|
Packit |
4e8bc4 |
size_t len = msg->msg_iov[i].iov_len;
|
|
Packit |
4e8bc4 |
to_copy = len < buf_remain ? len : buf_remain;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
memcpy(bp + bytes, (void*)msg->msg_iov[i].iov_base, to_copy);
|
|
Packit |
4e8bc4 |
buf_remain -= to_copy;
|
|
Packit |
4e8bc4 |
bytes += to_copy;
|
|
Packit |
4e8bc4 |
if (buf_remain == 0)
|
|
Packit |
4e8bc4 |
break;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
/* TODO : document the state machine interactions for SSL_write with
|
|
Packit |
4e8bc4 |
non-blocking sockets/ SSL re-negotiations
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
return SSL_write(c->ssl, c->ssl_wbuf, bytes);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* Writes data to the underlying BIO write buffers,
|
|
Packit |
4e8bc4 |
* which encrypt and write them to the socket.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
ssize_t ssl_write(conn *c, void *buf, size_t count) {
|
|
Packit |
4e8bc4 |
assert (c != NULL);
|
|
Packit |
4e8bc4 |
return SSL_write(c->ssl, buf, count);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* Loads server certificates to the SSL context and validate them.
|
|
Packit |
4e8bc4 |
* @return whether certificates are successfully loaded and verified or not.
|
|
Packit |
4e8bc4 |
* @param error_msg contains the error when unsuccessful.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
static bool load_server_certificates(char **errmsg) {
|
|
Packit |
4e8bc4 |
bool success = true;
|
|
Packit |
4e8bc4 |
char *error_msg = malloc(MAXPATHLEN + MAX_ERROR_MSG_SIZE);
|
|
Packit |
4e8bc4 |
size_t errmax = MAXPATHLEN + MAX_ERROR_MSG_SIZE - 1;
|
|
Packit |
4e8bc4 |
if (error_msg == NULL) {
|
|
Packit |
4e8bc4 |
*errmsg = NULL;
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
836412 |
if (settings.ssl_ctx == NULL) {
|
|
Packit |
836412 |
snprintf(error_msg, errmax, "Error TLS not enabled\r\n");
|
|
Packit |
836412 |
*errmsg = error_msg;
|
|
Packit |
836412 |
return false;
|
|
Packit |
836412 |
}
|
|
Packit |
4e8bc4 |
SSL_LOCK();
|
|
Packit |
4e8bc4 |
if (!SSL_CTX_use_certificate_chain_file(settings.ssl_ctx,
|
|
Packit |
4e8bc4 |
settings.ssl_chain_cert)) {
|
|
Packit |
4e8bc4 |
snprintf(error_msg, errmax, "Error loading the certificate chain: %s\r\n",
|
|
Packit |
4e8bc4 |
settings.ssl_chain_cert);
|
|
Packit |
4e8bc4 |
success = false;
|
|
Packit |
4e8bc4 |
} else if (!SSL_CTX_use_PrivateKey_file(settings.ssl_ctx, settings.ssl_key,
|
|
Packit |
4e8bc4 |
settings.ssl_keyformat)) {
|
|
Packit |
4e8bc4 |
snprintf(error_msg, errmax, "Error loading the key: %s\r\n", settings.ssl_key);
|
|
Packit |
4e8bc4 |
success = false;
|
|
Packit |
4e8bc4 |
} else if (!SSL_CTX_check_private_key(settings.ssl_ctx)) {
|
|
Packit |
4e8bc4 |
snprintf(error_msg, errmax, "Error validating the certificate\r\n");
|
|
Packit |
4e8bc4 |
success = false;
|
|
Packit |
4e8bc4 |
} else if (settings.ssl_ca_cert) {
|
|
Packit |
4e8bc4 |
if (!SSL_CTX_load_verify_locations(settings.ssl_ctx,
|
|
Packit |
4e8bc4 |
settings.ssl_ca_cert, NULL)) {
|
|
Packit |
4e8bc4 |
snprintf(error_msg, errmax,
|
|
Packit |
4e8bc4 |
"Error loading the CA certificate: %s\r\n", settings.ssl_ca_cert);
|
|
Packit |
4e8bc4 |
success = false;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
SSL_CTX_set_client_CA_list(settings.ssl_ctx,
|
|
Packit |
4e8bc4 |
SSL_load_client_CA_file(settings.ssl_ca_cert));
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
SSL_UNLOCK();
|
|
Packit |
4e8bc4 |
if (success) {
|
|
Packit |
4e8bc4 |
settings.ssl_last_cert_refresh_time = current_time;
|
|
Packit |
4e8bc4 |
free(error_msg);
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
*errmsg = error_msg;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return success;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* Verify SSL settings and initiates the SSL context.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
int ssl_init(void) {
|
|
Packit |
4e8bc4 |
assert(settings.ssl_enabled);
|
|
Packit |
4e8bc4 |
// SSL context for the process. All connections will share one
|
|
Packit |
4e8bc4 |
// process level context.
|
|
Packit |
4e8bc4 |
settings.ssl_ctx = SSL_CTX_new(TLS_server_method());
|
|
Packit |
4e8bc4 |
// Clients should use at least TLSv1.2
|
|
Packit |
4e8bc4 |
int flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
Packit |
4e8bc4 |
SSL_OP_NO_TLSv1 |SSL_OP_NO_TLSv1_1;
|
|
Packit |
4e8bc4 |
SSL_CTX_set_options(settings.ssl_ctx, flags);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// The server certificate, private key and validations.
|
|
Packit |
4e8bc4 |
char *error_msg;
|
|
Packit |
4e8bc4 |
if (!load_server_certificates(&error_msg)) {
|
|
Packit |
4e8bc4 |
if (settings.verbose) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "%s", error_msg);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
free(error_msg);
|
|
Packit |
4e8bc4 |
exit(EX_USAGE);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// The verification mode of client certificate, default is SSL_VERIFY_PEER.
|
|
Packit |
4e8bc4 |
SSL_CTX_set_verify(settings.ssl_ctx, settings.ssl_verify_mode, NULL);
|
|
Packit |
4e8bc4 |
if (settings.ssl_ciphers && !SSL_CTX_set_cipher_list(settings.ssl_ctx,
|
|
Packit |
4e8bc4 |
settings.ssl_ciphers)) {
|
|
Packit |
4e8bc4 |
if (settings.verbose) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "Error setting the provided cipher(s): %s\n",
|
|
Packit |
4e8bc4 |
settings.ssl_ciphers);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
exit(EX_USAGE);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return 0;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/*
|
|
Packit |
4e8bc4 |
* This method is registered with each SSL connection and abort the SSL session
|
|
Packit |
4e8bc4 |
* if a client initiates a renegotiation.
|
|
Packit |
4e8bc4 |
* TODO : Proper way to do this is to set SSL_OP_NO_RENEGOTIATION
|
|
Packit |
4e8bc4 |
* using the SSL_CTX_set_options but that option only available in
|
|
Packit |
4e8bc4 |
* openssl 1.1.0h or above.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
void ssl_callback(const SSL *s, int where, int ret) {
|
|
Packit |
4e8bc4 |
SSL* ssl = (SSL*)s;
|
|
Packit |
4e8bc4 |
if (SSL_in_before(ssl)) {
|
|
Packit |
4e8bc4 |
if (settings.verbose) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "%d: SSL renegotiation is not supported, "
|
|
Packit |
4e8bc4 |
"closing the connection\n", SSL_get_fd(ssl));
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
|
Packit |
4e8bc4 |
return;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool refresh_certs(char **errmsg) {
|
|
Packit |
4e8bc4 |
return load_server_certificates(errmsg);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
#endif
|