Blame genhash/ssl.c

Packit c22fc9
/*
Packit c22fc9
 * Soft:        Perform a GET query to a remote HTTP/HTTPS server.
Packit c22fc9
 *              Set a timer to compute global remote server response
Packit c22fc9
 *              time.
Packit c22fc9
 *
Packit c22fc9
 * Part:        SSL engine. 'Semi' asyncrhonous stream handling.
Packit c22fc9
 *
Packit c22fc9
 * Authors:     Alexandre Cassen, <acassen@linux-vs.org>
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
/* system includes */
Packit c22fc9
#include <openssl/err.h>
Packit c22fc9
#include <stdbool.h>
Packit c22fc9
Packit c22fc9
/* keepalived includes */
Packit c22fc9
#include "utils.h"
Packit c22fc9
Packit c22fc9
/* genhash includes */
Packit c22fc9
#include "include/ssl.h"
Packit c22fc9
#include "include/main.h"
Packit c22fc9
Packit c22fc9
/* extern variables */
Packit c22fc9
extern REQ *req;
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Initialize the SSL context, with or without specific
Packit c22fc9
 * configuration files.
Packit c22fc9
 */
Packit c22fc9
void
Packit c22fc9
init_ssl(void)
Packit c22fc9
{
Packit c22fc9
	/* Library initialization */
Packit Service ada993
#if HAVE_OPENSSL_INIT_CRYPTO
Packit c22fc9
	if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL))
Packit c22fc9
		fprintf(stderr, "OPENSSL_init_crypto failed\n");
Packit c22fc9
#else
Packit c22fc9
	SSL_library_init();
Packit c22fc9
	SSL_load_error_strings();
Packit c22fc9
#endif
Packit c22fc9
Packit c22fc9
	/* Initialize SSL context */
Packit Service ada993
#if HAVE_TLS_METHOD
Packit c22fc9
	req->meth = TLS_method();
Packit c22fc9
#else
Packit c22fc9
	req->meth = SSLv23_method();
Packit c22fc9
#endif
Packit c22fc9
	if (!(req->ctx = SSL_CTX_new(req->meth))) {
Packit c22fc9
		fprintf(stderr, "SSL_CTX_new() failed\n");
Packit c22fc9
		exit(1);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
#if HAVE_SSL_CTX_SET_VERIFY_DEPTH
Packit c22fc9
	SSL_CTX_set_verify_depth(req->ctx, 1);
Packit c22fc9
#endif
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
		fprintf(stderr, "  SSL error: (zero return)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_WANT_READ:
Packit c22fc9
		fprintf(stderr, "  SSL error: (read error)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_WANT_WRITE:
Packit c22fc9
		fprintf(stderr, "  SSL error: (write error)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_WANT_CONNECT:
Packit c22fc9
		fprintf(stderr, "  SSL error: (connect error)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_WANT_X509_LOOKUP:
Packit c22fc9
		fprintf(stderr, "  SSL error: (X509 lookup error)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_SYSCALL:
Packit c22fc9
		fprintf(stderr, "  SSL error: (syscall error)\n");
Packit c22fc9
		break;
Packit c22fc9
	case SSL_ERROR_SSL:
Packit c22fc9
		fprintf(stderr, "  SSL error: (%s)\n", ERR_error_string(ERR_get_error(), NULL));
Packit c22fc9
		break;
Packit c22fc9
	}
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
bool
Packit c22fc9
ssl_connect(thread_t * thread)
Packit c22fc9
{
Packit c22fc9
	SOCK *sock_obj = THREAD_ARG(thread);
Packit c22fc9
	int ret;
Packit c22fc9
Packit c22fc9
	sock_obj->ssl = SSL_new(req->ctx);
Packit c22fc9
	if (!sock_obj->ssl) {
Packit c22fc9
		fprintf(stderr, "SSL_new() failed\n");
Packit c22fc9
		return false;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	sock_obj->bio = BIO_new_socket(sock_obj->fd, BIO_NOCLOSE);
Packit c22fc9
	if (!sock_obj->bio) {
Packit c22fc9
		fprintf(stderr, "BIO_new_socket failed\n");
Packit c22fc9
		return false;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	BIO_set_nbio(sock_obj->bio, 1);	/* Set the Non-Blocking flag */
Packit Service ada993
#if HAVE_SSL_SET0_RBIO
Packit c22fc9
	BIO_up_ref(sock_obj->bio);
Packit c22fc9
	SSL_set0_rbio(sock_obj->ssl, sock_obj->bio);
Packit c22fc9
	SSL_set0_wbio(sock_obj->ssl, sock_obj->bio);
Packit c22fc9
#else
Packit c22fc9
	SSL_set_bio(sock_obj->ssl, sock_obj->bio, sock_obj->bio);
Packit c22fc9
#endif
Packit c22fc9
#ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_
Packit c22fc9
		if (req->vhost != NULL && req->sni) {
Packit c22fc9
			SSL_set_tlsext_host_name(sock_obj->ssl, req->vhost);
Packit c22fc9
		}
Packit c22fc9
#endif
Packit c22fc9
Packit c22fc9
	ret = SSL_connect(sock_obj->ssl);
Packit c22fc9
Packit c22fc9
	DBG("  SSL_connect return code = %d on fd:%d\n", ret, thread->u.fd);
Packit c22fc9
	ssl_printerr(SSL_get_error(sock_obj->ssl, ret));
Packit c22fc9
Packit c22fc9
	return (ret > 0);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
int
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 (1) {
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) ? 1 : 0;
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
	SOCK *sock_obj = THREAD_ARG(thread);
Packit c22fc9
	int r = 0;
Packit c22fc9
	int error;
Packit c22fc9
Packit c22fc9
	/* Handle read timeout */
Packit c22fc9
	if (thread->type == THREAD_READ_TIMEOUT) {
Packit c22fc9
		exit_code = 1;
Packit c22fc9
		return epilog(thread);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/*
Packit c22fc9
	 * The design implemented here is a workaround for use
Packit c22fc9
	 * with OpenSSL. This goto loop is a 'read until not
Packit c22fc9
	 * end of stream'. But this break a little our global
Packit c22fc9
	 * I/O multiplexer thread framework because it enter
Packit c22fc9
	 * a synchronous read process for each GET reply.
Packit c22fc9
	 * Sound a little nasty !.
Packit c22fc9
	 *
Packit c22fc9
	 * Why OpenSSL doesn t handle underlying fd. This
Packit c22fc9
	 * break the I/O (select()) approach !...
Packit c22fc9
	 * If you read this and know the answer, please reply
Packit c22fc9
	 * I am probably missing something... :)
Packit c22fc9
	 * My test show that sometime it return from select,
Packit c22fc9
	 * and sometime not...
Packit c22fc9
	 */
Packit c22fc9
Packit c22fc9
      read_stream:
Packit c22fc9
Packit c22fc9
	/* read the SSL stream */
Packit c22fc9
	r = MAX_BUFFER_LENGTH - sock_obj->size;
Packit c22fc9
	if (r <= 0) {
Packit c22fc9
		/* defensive check, should not occur */
Packit c22fc9
		fprintf(stderr, "SSL socket buffer overflow (not consumed)\n");
Packit c22fc9
		r = MAX_BUFFER_LENGTH;
Packit c22fc9
	}
Packit c22fc9
	memset(sock_obj->buffer + sock_obj->size, 0, (size_t)r);
Packit c22fc9
	r = SSL_read(sock_obj->ssl, sock_obj->buffer + sock_obj->size, r);
Packit c22fc9
	error = SSL_get_error(sock_obj->ssl, r);
Packit c22fc9
Packit c22fc9
	DBG(" [l:%d,fd:%d]\n", r, sock_obj->fd);
Packit c22fc9
Packit c22fc9
	if (error) {
Packit c22fc9
		/* All the SSL streal has been parsed */
Packit c22fc9
		/* Handle response stream */
Packit c22fc9
		if (error != SSL_ERROR_NONE)
Packit c22fc9
			return finalize(thread);
Packit c22fc9
	} else if (r > 0 && error == 0) {
Packit c22fc9
Packit c22fc9
		/* Handle the response stream */
Packit c22fc9
		http_process_stream(sock_obj, r);
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
		goto read_stream;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}