Home | History | Annotate | Line # | Download | only in dist
sslutils.c revision 1.2
      1  1.1  christos /*
      2  1.1  christos  * Copyright (c) 2002 - 2003
      3  1.1  christos  * NetGroup, Politecnico di Torino (Italy)
      4  1.1  christos  * All rights reserved.
      5  1.1  christos  *
      6  1.1  christos  * Redistribution and use in source and binary forms, with or without
      7  1.1  christos  * modification, are permitted provided that the following conditions
      8  1.1  christos  * are met:
      9  1.1  christos  *
     10  1.1  christos  * 1. Redistributions of source code must retain the above copyright
     11  1.1  christos  * notice, this list of conditions and the following disclaimer.
     12  1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1  christos  * notice, this list of conditions and the following disclaimer in the
     14  1.1  christos  * documentation and/or other materials provided with the distribution.
     15  1.1  christos  * 3. Neither the name of the Politecnico di Torino nor the names of its
     16  1.1  christos  * contributors may be used to endorse or promote products derived from
     17  1.1  christos  * this software without specific prior written permission.
     18  1.1  christos  *
     19  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  1.1  christos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  1.1  christos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  1.1  christos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  1.1  christos  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  1.1  christos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  1.1  christos  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  1.1  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  1.1  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  1.1  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  1.1  christos  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  1.1  christos  *
     31  1.1  christos  */
     32  1.1  christos 
     33  1.1  christos #ifdef HAVE_CONFIG_H
     34  1.1  christos #include <config.h>
     35  1.1  christos #endif
     36  1.1  christos 
     37  1.1  christos #ifdef HAVE_OPENSSL
     38  1.1  christos #include <stdlib.h>
     39  1.1  christos 
     40  1.2  christos #include "sslutils.h"
     41  1.1  christos #include "portability.h"
     42  1.1  christos 
     43  1.1  christos 
     44  1.1  christos static const char *ssl_keyfile = "";   //!< file containing the private key in PEM format
     45  1.1  christos static const char *ssl_certfile = "";  //!< file containing the server's certificate in PEM format
     46  1.1  christos static const char *ssl_rootfile = "";  //!< file containing the list of CAs trusted by the client
     47  1.1  christos // TODO: a way to set ssl_rootfile from the command line, or an envvar?
     48  1.1  christos 
     49  1.1  christos // TODO: lock?
     50  1.1  christos static SSL_CTX *ctx;
     51  1.1  christos 
     52  1.1  christos void ssl_set_certfile(const char *certfile)
     53  1.1  christos {
     54  1.1  christos 	ssl_certfile = certfile;
     55  1.1  christos }
     56  1.1  christos 
     57  1.1  christos void ssl_set_keyfile(const char *keyfile)
     58  1.1  christos {
     59  1.1  christos 	ssl_keyfile = keyfile;
     60  1.1  christos }
     61  1.1  christos 
     62  1.1  christos int ssl_init_once(int is_server, int enable_compression, char *errbuf, size_t errbuflen)
     63  1.1  christos {
     64  1.1  christos 	static int inited = 0;
     65  1.1  christos 	if (inited) return 0;
     66  1.1  christos 
     67  1.1  christos 	SSL_library_init();
     68  1.1  christos 	SSL_load_error_strings();
     69  1.1  christos 	OpenSSL_add_ssl_algorithms();
     70  1.1  christos 	if (enable_compression)
     71  1.1  christos 		SSL_COMP_get_compression_methods();
     72  1.1  christos 
     73  1.1  christos 	SSL_METHOD const *meth =
     74  1.1  christos 	    is_server ? SSLv23_server_method() : SSLv23_client_method();
     75  1.1  christos 	ctx = SSL_CTX_new(meth);
     76  1.1  christos 	if (! ctx)
     77  1.1  christos 	{
     78  1.1  christos 		snprintf(errbuf, errbuflen, "Cannot get a new SSL context: %s", ERR_error_string(ERR_get_error(), NULL));
     79  1.1  christos 		goto die;
     80  1.1  christos 	}
     81  1.1  christos 
     82  1.1  christos 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
     83  1.1  christos 
     84  1.1  christos 	if (is_server)
     85  1.1  christos 	{
     86  1.1  christos 		char const *certfile = ssl_certfile[0] ? ssl_certfile : "cert.pem";
     87  1.1  christos 		if (1 != SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
     88  1.1  christos 		{
     89  1.1  christos 			snprintf(errbuf, errbuflen, "Cannot read certificate file %s: %s", certfile, ERR_error_string(ERR_get_error(), NULL));
     90  1.1  christos 			goto die;
     91  1.1  christos 		}
     92  1.1  christos 
     93  1.1  christos 		char const *keyfile = ssl_keyfile[0] ? ssl_keyfile : "key.pem";
     94  1.1  christos 		if (1 != SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
     95  1.1  christos 		{
     96  1.1  christos 			snprintf(errbuf, errbuflen, "Cannot read private key file %s: %s", keyfile, ERR_error_string(ERR_get_error(), NULL));
     97  1.1  christos 			goto die;
     98  1.1  christos 		}
     99  1.1  christos 	}
    100  1.1  christos 	else
    101  1.1  christos 	{
    102  1.1  christos 		if (ssl_rootfile[0])
    103  1.1  christos 		{
    104  1.1  christos 			if (! SSL_CTX_load_verify_locations(ctx, ssl_rootfile, 0))
    105  1.1  christos 			{
    106  1.1  christos 				snprintf(errbuf, errbuflen, "Cannot read CA list from %s", ssl_rootfile);
    107  1.1  christos 				goto die;
    108  1.1  christos 			}
    109  1.1  christos 		}
    110  1.1  christos 		else
    111  1.1  christos 		{
    112  1.1  christos 			SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
    113  1.1  christos 		}
    114  1.1  christos 	}
    115  1.1  christos 
    116  1.1  christos #if 0
    117  1.1  christos 	if (! RAND_load_file(RANDOM, 1024*1024))
    118  1.1  christos 	{
    119  1.1  christos 		snprintf(errbuf, errbuflen, "Cannot init random");
    120  1.1  christos 		goto die;
    121  1.1  christos 	}
    122  1.1  christos 
    123  1.1  christos 	if (is_server)
    124  1.1  christos 	{
    125  1.1  christos 		SSL_CTX_set_session_id_context(ctx, (void *)&s_server_session_id_context, sizeof(s_server_session_id_context));
    126  1.1  christos 	}
    127  1.1  christos #endif
    128  1.1  christos 
    129  1.1  christos 	inited = 1;
    130  1.1  christos 	return 0;
    131  1.1  christos 
    132  1.1  christos die:
    133  1.1  christos 	return -1;
    134  1.1  christos }
    135  1.1  christos 
    136  1.1  christos SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen)
    137  1.1  christos {
    138  1.1  christos 	if (ssl_init_once(is_server, 1, errbuf, errbuflen) < 0) {
    139  1.1  christos 		return NULL;
    140  1.1  christos 	}
    141  1.1  christos 
    142  1.1  christos 	SSL *ssl = SSL_new(ctx); // TODO: also a DTLS context
    143  1.1  christos 	SSL_set_fd(ssl, (int)s);
    144  1.1  christos 
    145  1.1  christos 	if (is_server) {
    146  1.1  christos 		if (SSL_accept(ssl) <= 0) {
    147  1.1  christos 			snprintf(errbuf, errbuflen, "SSL_accept(): %s",
    148  1.1  christos 					ERR_error_string(ERR_get_error(), NULL));
    149  1.1  christos 			return NULL;
    150  1.1  christos 		}
    151  1.1  christos 	} else {
    152  1.1  christos 		if (SSL_connect(ssl) <= 0) {
    153  1.1  christos 			snprintf(errbuf, errbuflen, "SSL_connect(): %s",
    154  1.1  christos 					ERR_error_string(ERR_get_error(), NULL));
    155  1.1  christos 			return NULL;
    156  1.1  christos 		}
    157  1.1  christos 	}
    158  1.1  christos 
    159  1.1  christos 	return ssl;
    160  1.1  christos }
    161  1.1  christos 
    162  1.1  christos // Finish using an SSL handle; shut down the connection and free the
    163  1.1  christos // handle.
    164  1.1  christos void ssl_finish(SSL *ssl)
    165  1.1  christos {
    166  1.1  christos 	//
    167  1.1  christos 	// We won't be using this again, so we can just send the
    168  1.1  christos 	// shutdown alert and free up the handle, and have our
    169  1.1  christos 	// caller close the socket.
    170  1.1  christos 	//
    171  1.1  christos 	// XXX - presumably, if the connection is shut down on
    172  1.1  christos 	// our side, either our peer won't have a problem sending
    173  1.1  christos 	// their shutdown alert or will not treat such a problem
    174  1.1  christos 	// as an error.  If this causes errors to be reported,
    175  1.1  christos 	// fix that as appropriate.
    176  1.1  christos 	//
    177  1.1  christos 	SSL_shutdown(ssl);
    178  1.1  christos 	SSL_free(ssl);
    179  1.1  christos }
    180  1.1  christos 
    181  1.1  christos // Same return value as sock_send:
    182  1.1  christos // 0 on OK, -1 on error but closed connection (-2).
    183  1.1  christos int ssl_send(SSL *ssl, char const *buffer, int size, char *errbuf, size_t errbuflen)
    184  1.1  christos {
    185  1.1  christos 	int status = SSL_write(ssl, buffer, size);
    186  1.1  christos 	if (status > 0)
    187  1.1  christos 	{
    188  1.1  christos 		// "SSL_write() will only return with success, when the complete contents (...) has been written."
    189  1.1  christos 		return 0;
    190  1.1  christos 	}
    191  1.1  christos 	else
    192  1.1  christos 	{
    193  1.1  christos 		int ssl_err = SSL_get_error(ssl, status); // TODO: does it pop the error?
    194  1.1  christos 		if (ssl_err == SSL_ERROR_ZERO_RETURN)
    195  1.1  christos 		{
    196  1.1  christos 			return -2;
    197  1.1  christos 		}
    198  1.1  christos 		else if (ssl_err == SSL_ERROR_SYSCALL)
    199  1.1  christos 		{
    200  1.1  christos #ifndef _WIN32
    201  1.1  christos 			if (errno == ECONNRESET || errno == EPIPE) return -2;
    202  1.1  christos #endif
    203  1.1  christos 		}
    204  1.1  christos 		snprintf(errbuf, errbuflen, "SSL_write(): %s",
    205  1.1  christos 		    ERR_error_string(ERR_get_error(), NULL));
    206  1.1  christos 		return -1;
    207  1.1  christos 	}
    208  1.1  christos }
    209  1.1  christos 
    210  1.1  christos // Returns the number of bytes read, or -1 on syserror, or -2 on SSL error.
    211  1.1  christos int ssl_recv(SSL *ssl, char *buffer, int size, char *errbuf, size_t errbuflen)
    212  1.1  christos {
    213  1.1  christos 	int status = SSL_read(ssl, buffer, size);
    214  1.1  christos 	if (status <= 0)
    215  1.1  christos 	{
    216  1.1  christos 		int ssl_err = SSL_get_error(ssl, status);
    217  1.1  christos 		if (ssl_err == SSL_ERROR_ZERO_RETURN)
    218  1.1  christos 		{
    219  1.1  christos 			return 0;
    220  1.1  christos 		}
    221  1.1  christos 		else if (ssl_err == SSL_ERROR_SYSCALL)
    222  1.1  christos 		{
    223  1.1  christos 			return -1;
    224  1.1  christos 		}
    225  1.1  christos 		else
    226  1.1  christos 		{
    227  1.1  christos 			// Should not happen
    228  1.1  christos 			snprintf(errbuf, errbuflen, "SSL_read(): %s",
    229  1.1  christos 			    ERR_error_string(ERR_get_error(), NULL));
    230  1.1  christos 			return -2;
    231  1.1  christos 		}
    232  1.1  christos 	}
    233  1.1  christos 	else
    234  1.1  christos 	{
    235  1.1  christos 		return status;
    236  1.1  christos 	}
    237  1.1  christos }
    238  1.1  christos 
    239  1.1  christos #endif // HAVE_OPENSSL
    240