|
Packit Service |
5956c7 |
/*
|
|
Packit Service |
5956c7 |
* Soft: Perform a GET query to a remote HTTP/HTTPS server.
|
|
Packit Service |
5956c7 |
* Set a timer to compute global remote server response
|
|
Packit Service |
5956c7 |
* time.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Part: SSL engine. 'Semi' asyncrhonous stream handling.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Authors: Alexandre Cassen, <acassen@linux-vs.org>
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
5956c7 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
5956c7 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit Service |
5956c7 |
* See the GNU General Public License for more details.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
5956c7 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
5956c7 |
* as published by the Free Software Foundation; either version
|
|
Packit Service |
5956c7 |
* 2 of the License, or (at your option) any later version.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#include "config.h"
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* system includes */
|
|
Packit Service |
5956c7 |
#include <openssl/err.h>
|
|
Packit Service |
5956c7 |
#include <stdbool.h>
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* keepalived includes */
|
|
Packit Service |
5956c7 |
#include "utils.h"
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* genhash includes */
|
|
Packit Service |
5956c7 |
#include "include/ssl.h"
|
|
Packit Service |
5956c7 |
#include "include/main.h"
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* extern variables */
|
|
Packit Service |
5956c7 |
extern REQ *req;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/*
|
|
Packit Service |
5956c7 |
* Initialize the SSL context, with or without specific
|
|
Packit Service |
5956c7 |
* configuration files.
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
init_ssl(void)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
/* Library initialization */
|
|
Packit Service |
3e6040 |
#ifdef HAVE_OPENSSL_INIT_CRYPTO
|
|
Packit Service |
0264bb |
#ifndef HAVE_OPENSSL_INIT_NO_LOAD_CONFIG_BUG
|
|
Packit Service |
0264bb |
/* In OpenSSL v1.1.1 if the following is called, SSL_CTX_new() below fails.
|
|
Packit Service |
0264bb |
* It works in v1.1.0h and v1.1.1b.
|
|
Packit Service |
0264bb |
* It transpires that it works without setting NO_LOAD_CONFIG, but it is
|
|
Packit Service |
0264bb |
* presumably more efficient not to load it. */
|
|
Packit Service |
5956c7 |
if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL))
|
|
Packit Service |
5956c7 |
fprintf(stderr, "OPENSSL_init_crypto failed\n");
|
|
Packit Service |
0264bb |
#endif
|
|
Packit Service |
5956c7 |
#else
|
|
Packit Service |
5956c7 |
SSL_library_init();
|
|
Packit Service |
5956c7 |
SSL_load_error_strings();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Initialize SSL context */
|
|
Packit Service |
3e6040 |
#ifdef HAVE_TLS_METHOD
|
|
Packit Service |
5956c7 |
req->meth = TLS_method();
|
|
Packit Service |
5956c7 |
#else
|
|
Packit Service |
5956c7 |
req->meth = SSLv23_method();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
if (!(req->ctx = SSL_CTX_new(req->meth))) {
|
|
Packit Service |
5956c7 |
fprintf(stderr, "SSL_CTX_new() failed\n");
|
|
Packit Service |
5956c7 |
exit(1);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#if HAVE_SSL_CTX_SET_VERIFY_DEPTH
|
|
Packit Service |
5956c7 |
SSL_CTX_set_verify_depth(req->ctx, 1);
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Display SSL error to readable string */
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
ssl_printerr(int err)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
switch (err) {
|
|
Packit Service |
5956c7 |
case SSL_ERROR_ZERO_RETURN:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (zero return)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_WANT_READ:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (read error)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_WANT_WRITE:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (write error)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_WANT_CONNECT:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (connect error)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (X509 lookup error)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_SYSCALL:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (syscall error)\n");
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
case SSL_ERROR_SSL:
|
|
Packit Service |
5956c7 |
fprintf(stderr, " SSL error: (%s)\n", ERR_error_string(ERR_get_error(), NULL));
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
bool
|
|
Packit Service |
5956c7 |
ssl_connect(thread_t * thread)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
SOCK *sock_obj = THREAD_ARG(thread);
|
|
Packit Service |
5956c7 |
int ret;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
sock_obj->ssl = SSL_new(req->ctx);
|
|
Packit Service |
5956c7 |
if (!sock_obj->ssl) {
|
|
Packit Service |
5956c7 |
fprintf(stderr, "SSL_new() failed\n");
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
sock_obj->bio = BIO_new_socket(sock_obj->fd, BIO_NOCLOSE);
|
|
Packit Service |
5956c7 |
if (!sock_obj->bio) {
|
|
Packit Service |
5956c7 |
fprintf(stderr, "BIO_new_socket failed\n");
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
BIO_set_nbio(sock_obj->bio, 1); /* Set the Non-Blocking flag */
|
|
Packit Service |
3e6040 |
#ifdef HAVE_SSL_SET0_RBIO
|
|
Packit Service |
5956c7 |
BIO_up_ref(sock_obj->bio);
|
|
Packit Service |
5956c7 |
SSL_set0_rbio(sock_obj->ssl, sock_obj->bio);
|
|
Packit Service |
5956c7 |
SSL_set0_wbio(sock_obj->ssl, sock_obj->bio);
|
|
Packit Service |
5956c7 |
#else
|
|
Packit Service |
5956c7 |
SSL_set_bio(sock_obj->ssl, sock_obj->bio, sock_obj->bio);
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
#ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_
|
|
Packit Service |
5956c7 |
if (req->vhost != NULL && req->sni) {
|
|
Packit Service |
5956c7 |
SSL_set_tlsext_host_name(sock_obj->ssl, req->vhost);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
ret = SSL_connect(sock_obj->ssl);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
DBG(" SSL_connect return code = %d on fd:%d\n", ret, thread->u.fd);
|
|
Packit Service |
5956c7 |
ssl_printerr(SSL_get_error(sock_obj->ssl, ret));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return (ret > 0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
ssl_send_request(SSL * ssl, char *str_request, int request_len)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int err, r = 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
while (1) {
|
|
Packit Service |
5956c7 |
err = 1;
|
|
Packit Service |
5956c7 |
r = SSL_write(ssl, str_request, request_len);
|
|
Packit Service |
5956c7 |
if (SSL_ERROR_NONE != SSL_get_error(ssl, r))
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
err++;
|
|
Packit Service |
5956c7 |
if (request_len != r)
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
err++;
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return (err == 3) ? 1 : 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Asynchronous SSL stream reader */
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
ssl_read_thread(thread_t * thread)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
SOCK *sock_obj = THREAD_ARG(thread);
|
|
Packit Service |
5956c7 |
int r = 0;
|
|
Packit Service |
5956c7 |
int error;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Handle read timeout */
|
|
Packit Service |
5956c7 |
if (thread->type == THREAD_READ_TIMEOUT) {
|
|
Packit Service |
5956c7 |
exit_code = 1;
|
|
Packit Service |
5956c7 |
return epilog(thread);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/*
|
|
Packit Service |
5956c7 |
* The design implemented here is a workaround for use
|
|
Packit Service |
5956c7 |
* with OpenSSL. This goto loop is a 'read until not
|
|
Packit Service |
5956c7 |
* end of stream'. But this break a little our global
|
|
Packit Service |
5956c7 |
* I/O multiplexer thread framework because it enter
|
|
Packit Service |
5956c7 |
* a synchronous read process for each GET reply.
|
|
Packit Service |
5956c7 |
* Sound a little nasty !.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Why OpenSSL doesn t handle underlying fd. This
|
|
Packit Service |
5956c7 |
* break the I/O (select()) approach !...
|
|
Packit Service |
5956c7 |
* If you read this and know the answer, please reply
|
|
Packit Service |
5956c7 |
* I am probably missing something... :)
|
|
Packit Service |
5956c7 |
* My test show that sometime it return from select,
|
|
Packit Service |
5956c7 |
* and sometime not...
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
read_stream:
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* read the SSL stream */
|
|
Packit Service |
5956c7 |
r = MAX_BUFFER_LENGTH - sock_obj->size;
|
|
Packit Service |
5956c7 |
if (r <= 0) {
|
|
Packit Service |
5956c7 |
/* defensive check, should not occur */
|
|
Packit Service |
5956c7 |
fprintf(stderr, "SSL socket buffer overflow (not consumed)\n");
|
|
Packit Service |
5956c7 |
r = MAX_BUFFER_LENGTH;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
memset(sock_obj->buffer + sock_obj->size, 0, (size_t)r);
|
|
Packit Service |
5956c7 |
r = SSL_read(sock_obj->ssl, sock_obj->buffer + sock_obj->size, r);
|
|
Packit Service |
5956c7 |
error = SSL_get_error(sock_obj->ssl, r);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
DBG(" [l:%d,fd:%d]\n", r, sock_obj->fd);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (error) {
|
|
Packit Service |
5956c7 |
/* All the SSL streal has been parsed */
|
|
Packit Service |
5956c7 |
/* Handle response stream */
|
|
Packit Service |
5956c7 |
if (error != SSL_ERROR_NONE)
|
|
Packit Service |
5956c7 |
return finalize(thread);
|
|
Packit Service |
5956c7 |
} else if (r > 0 && error == 0) {
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Handle the response stream */
|
|
Packit Service |
5956c7 |
http_process_stream(sock_obj, r);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/*
|
|
Packit Service |
5956c7 |
* Register next ssl stream reader.
|
|
Packit Service |
5956c7 |
* Register itself to not perturbe global I/O multiplexer.
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
goto read_stream;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|