|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Soft: Keepalived is a failover program for the LVS project
|
|
Packit |
c22fc9 |
* <www.linuxvirtualserver.org>. It monitor & manipulate
|
|
Packit |
c22fc9 |
* a loadbalanced server pool using multi-layer checks.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Part: SSL GET CHECK. Perform an ssl get query to a specified
|
|
Packit |
c22fc9 |
* url, compute a MD5 over this result and match it to the
|
|
Packit |
c22fc9 |
* expected value.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Authors: Alexandre Cassen, <acassen@linux-vs.org>
|
|
Packit |
c22fc9 |
* Jan Holmberg, <jan@artech.net>
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
c22fc9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
c22fc9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
c22fc9 |
* See the GNU General Public License for more details.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
c22fc9 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
c22fc9 |
* as published by the Free Software Foundation; either version
|
|
Packit |
c22fc9 |
* 2 of the License, or (at your option) any later version.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "config.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include <fcntl.h>
|
|
Packit |
c22fc9 |
#include <openssl/err.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "check_ssl.h"
|
|
Packit |
c22fc9 |
#include "check_api.h"
|
|
Packit |
c22fc9 |
#include "check_http.h"
|
|
Packit |
c22fc9 |
#include "logger.h"
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
#include "scheduler.h"
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* SSL primitives */
|
|
Packit |
c22fc9 |
/* Free an SSL context */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
clear_ssl(ssl_data_t *ssl)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (ssl && ssl->ctx) {
|
|
Packit |
c22fc9 |
SSL_CTX_free(ssl->ctx);
|
|
Packit |
c22fc9 |
ssl->ctx = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* PEM password callback function */
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
password_cb(char *buf, int num, __attribute__((unused)) int rwflag, void *userdata)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ssl_data_t *ssl = (ssl_data_t *) userdata;
|
|
Packit |
c22fc9 |
size_t plen = strlen(ssl->password);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if ((unsigned)num < plen + 1)
|
|
Packit |
c22fc9 |
return (0);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
strcpy(buf, ssl->password);
|
|
Packit |
c22fc9 |
return (int)plen;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Inititalize global SSL context */
|
|
Packit |
c22fc9 |
static bool
|
|
Packit |
c22fc9 |
build_ssl_ctx(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ssl_data_t *ssl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Library initialization */
|
|
Packit Service |
8fc997 |
#if HAVE_OPENSSL_INIT_CRYPTO
|
|
Packit Service |
a07680 |
#ifndef HAVE_OPENSSL_INIT_NO_LOAD_CONFIG_BUG
|
|
Packit Service |
a07680 |
/* In OpenSSL v1.1.1 if the following is called, SSL_CTX_new() below fails.
|
|
Packit Service |
a07680 |
* It works in v1.1.0h and v1.1.1b.
|
|
Packit Service |
a07680 |
* It transpires that it works without setting NO_LOAD_CONFIG, but it is
|
|
Packit Service |
a07680 |
* presumably more efficient not to load it. */
|
|
Packit |
c22fc9 |
if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL))
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "OPENSSL_init_crypto failed");
|
|
Packit Service |
a07680 |
#endif
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
SSL_library_init();
|
|
Packit |
c22fc9 |
SSL_load_error_strings();
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!check_data->ssl)
|
|
Packit |
c22fc9 |
ssl = (ssl_data_t *) MALLOC(sizeof(ssl_data_t));
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
ssl = check_data->ssl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Initialize SSL context */
|
|
Packit Service |
8fc997 |
#if HAVE_TLS_METHOD
|
|
Packit |
c22fc9 |
ssl->meth = TLS_method();
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
ssl->meth = SSLv23_method();
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
if (!(ssl->ctx = SSL_CTX_new(ssl->meth))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "SSL error: cannot create new SSL context");
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* return for autogen context */
|
|
Packit |
c22fc9 |
if (!check_data->ssl) {
|
|
Packit |
c22fc9 |
check_data->ssl = ssl;
|
|
Packit |
c22fc9 |
goto end;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Load our keys and certificates */
|
|
Packit |
c22fc9 |
if (check_data->ssl->certfile)
|
|
Packit |
c22fc9 |
if (!
|
|
Packit |
c22fc9 |
(SSL_CTX_use_certificate_chain_file
|
|
Packit |
c22fc9 |
(ssl->ctx, check_data->ssl->certfile))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO,
|
|
Packit |
c22fc9 |
"SSL error : Cant load certificate file...");
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle password callback using userdata ssl */
|
|
Packit |
c22fc9 |
if (check_data->ssl->password) {
|
|
Packit |
c22fc9 |
SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx,
|
|
Packit |
c22fc9 |
check_data->ssl);
|
|
Packit |
c22fc9 |
SSL_CTX_set_default_passwd_cb(ssl->ctx, password_cb);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (check_data->ssl->keyfile)
|
|
Packit |
c22fc9 |
if (!
|
|
Packit |
c22fc9 |
(SSL_CTX_use_PrivateKey_file
|
|
Packit |
c22fc9 |
(ssl->ctx, check_data->ssl->keyfile, SSL_FILETYPE_PEM))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "SSL error : Cant load key file...");
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Load the CAs we trust */
|
|
Packit |
c22fc9 |
if (check_data->ssl->cafile)
|
|
Packit |
c22fc9 |
if (!
|
|
Packit |
c22fc9 |
(SSL_CTX_load_verify_locations
|
|
Packit |
c22fc9 |
(ssl->ctx, check_data->ssl->cafile, 0))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "SSL error : Cant load CA file...");
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
end:
|
|
Packit |
c22fc9 |
#if HAVE_SSL_CTX_SET_VERIFY_DEPTH
|
|
Packit |
c22fc9 |
SSL_CTX_set_verify_depth(ssl->ctx, 1);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Initialize the SSL context, with or without specific
|
|
Packit |
c22fc9 |
* configuration files.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
init_ssl_ctx(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ssl_data_t *ssl = check_data->ssl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!build_ssl_ctx()) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Error Initialize SSL, ctx Instance");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL keyfile:%s", ssl->keyfile);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL password:%s", ssl->password);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL cafile:%s", ssl->cafile);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Terminate...");
|
|
Packit |
c22fc9 |
clear_ssl(ssl);
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Display SSL error to readable string */
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
ssl_printerr(int err)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
switch (err) {
|
|
Packit |
c22fc9 |
case SSL_ERROR_ZERO_RETURN:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (zero return)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_WANT_READ:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (read error)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_WANT_WRITE:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (write error)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_WANT_CONNECT:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (connect error)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (X509 lookup error)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_SYSCALL:
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (syscall error)");
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case SSL_ERROR_SSL:
|
|
Packit |
c22fc9 |
/* Note: the following is not thread safe. Use MALLOC(256) and ERR_error_string_n if need thread safety */
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " SSL error: (%s)", ERR_error_string(ERR_get_error(), NULL));
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
ssl_connect(thread_t * thread, int new_req)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
checker_t *checker = THREAD_ARG(thread);
|
|
Packit |
c22fc9 |
http_checker_t *http_get_check = CHECKER_ARG(checker);
|
|
Packit |
c22fc9 |
request_t *req = http_get_check->req;
|
|
Packit |
c22fc9 |
#ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_
|
|
Packit |
c22fc9 |
url_t *url = list_element(http_get_check->url, http_get_check->url_it);
|
|
Packit |
c22fc9 |
char* vhost = NULL;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
int ret = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* First round, create SSL context */
|
|
Packit |
c22fc9 |
if (new_req) {
|
|
Packit |
c22fc9 |
int bio_fd;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(req->ssl = SSL_new(check_data->ssl->ctx))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Unable to establish ssl connection - SSL_new() failed");
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(req->bio = BIO_new_socket(thread->u.fd, BIO_NOCLOSE))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Unable to establish ssl connection - BIO_new_socket() failed");
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
BIO_get_fd(req->bio, &bio_fd);
|
|
Packit |
c22fc9 |
fcntl(bio_fd, F_SETFD, fcntl(bio_fd, F_GETFD) | FD_CLOEXEC);
|
|
Packit Service |
8fc997 |
#if HAVE_SSL_SET0_RBIO
|
|
Packit |
c22fc9 |
BIO_up_ref(req->bio);
|
|
Packit |
c22fc9 |
SSL_set0_rbio(req->ssl, req->bio);
|
|
Packit |
c22fc9 |
SSL_set0_wbio(req->ssl, req->bio);
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
SSL_set_bio(req->ssl, req->bio, req->bio);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_
|
|
Packit |
c22fc9 |
if (http_get_check->enable_sni) {
|
|
Packit |
c22fc9 |
if (url && url->virtualhost)
|
|
Packit |
c22fc9 |
vhost = url->virtualhost;
|
|
Packit |
c22fc9 |
else if (http_get_check->virtualhost)
|
|
Packit |
c22fc9 |
vhost = http_get_check->virtualhost;
|
|
Packit |
c22fc9 |
else if (checker->vs->virtualhost)
|
|
Packit |
c22fc9 |
vhost = checker->vs->virtualhost;
|
|
Packit |
c22fc9 |
if (vhost)
|
|
Packit |
c22fc9 |
SSL_set_tlsext_host_name(req->ssl, vhost);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ret = SSL_connect(req->ssl);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return ret;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
ssl_send_request(SSL * ssl, char *str_request, int request_len)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
int err, r = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
while (true) {
|
|
Packit |
c22fc9 |
err = 1;
|
|
Packit |
c22fc9 |
r = SSL_write(ssl, str_request, request_len);
|
|
Packit |
c22fc9 |
if (SSL_ERROR_NONE != SSL_get_error(ssl, r))
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
err++;
|
|
Packit |
c22fc9 |
if (request_len != r)
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
err++;
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return (err == 3);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Asynchronous SSL stream reader */
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
ssl_read_thread(thread_t * thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
checker_t *checker = THREAD_ARG(thread);
|
|
Packit |
c22fc9 |
http_checker_t *http_get_check = CHECKER_ARG(checker);
|
|
Packit |
c22fc9 |
request_t *req = http_get_check->req;
|
|
Packit |
c22fc9 |
url_t *url = list_element(http_get_check->url, http_get_check->url_it);
|
|
Packit |
c22fc9 |
unsigned timeout = checker->co->connection_to;
|
|
Packit |
c22fc9 |
unsigned char digest[MD5_DIGEST_LENGTH];
|
|
Packit |
c22fc9 |
int r = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle read timeout */
|
|
Packit |
c22fc9 |
if (thread->type == THREAD_READ_TIMEOUT && !req->extracted)
|
|
Packit |
c22fc9 |
return timeout_epilog(thread, "Timeout SSL read");
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* read the SSL stream */
|
|
Packit |
c22fc9 |
r = SSL_read(req->ssl, req->buffer + req->len, (int)(MAX_BUFFER_LENGTH - req->len));
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
req->error = SSL_get_error(req->ssl, r);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (req->error == SSL_ERROR_WANT_READ) {
|
|
Packit |
c22fc9 |
/* async read unfinished */
|
|
Packit |
c22fc9 |
thread_add_read(thread->master, ssl_read_thread, checker,
|
|
Packit |
c22fc9 |
thread->u.fd, timeout);
|
|
Packit |
c22fc9 |
} else if (r > 0 && req->error == 0) {
|
|
Packit |
c22fc9 |
/* Handle response stream */
|
|
Packit |
c22fc9 |
http_process_response(req, (size_t)r, url);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Register next ssl stream reader.
|
|
Packit |
c22fc9 |
* Register itself to not perturbe global I/O multiplexer.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
thread_add_read(thread->master, ssl_read_thread, checker,
|
|
Packit |
c22fc9 |
thread->u.fd, timeout);
|
|
Packit |
c22fc9 |
} else if (req->error) {
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* All the SSL streal has been parsed */
|
|
Packit |
c22fc9 |
if (url->digest)
|
|
Packit |
c22fc9 |
MD5_Final(digest, &req->context);
|
|
Packit |
c22fc9 |
SSL_set_quiet_shutdown(req->ssl, 1);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
r = (req->error == SSL_ERROR_ZERO_RETURN) ? SSL_shutdown(req->ssl) : 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (r && !req->extracted) {
|
|
Packit |
c22fc9 |
return timeout_epilog(thread, "SSL read error from");
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle response stream */
|
|
Packit |
c22fc9 |
http_handle_response(thread, digest, !req->extracted);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
register_check_ssl_addresses(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
register_thread_address("ssl_read_thread", ssl_read_thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|