stunnel4/src/ssl.c

249 lines
8.8 KiB
C

/*
* stunnel Universal SSL tunnel
* Copyright (C) 1998-2012 Michal Trojnara <Michal.Trojnara@mirt.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses>.
*
* Linking stunnel statically or dynamically with other modules is making
* a combined work based on stunnel. Thus, the terms and conditions of
* the GNU General Public License cover the whole combination.
*
* In addition, as a special exception, the copyright holder of stunnel
* gives you permission to combine stunnel with free software programs or
* libraries that are released under the GNU LGPL and with code included
* in the standard release of OpenSSL under the OpenSSL License (or
* modified versions of such code, with unchanged license). You may copy
* and distribute such a system following the terms of the GNU GPL for
* stunnel and the licenses of the other code concerned.
*
* Note that people who make modified versions of stunnel are not obligated
* to grant this special exception for their modified versions; it is their
* choice whether to do so. The GNU General Public License gives permission
* to release a modified version without this exception; this exception
* also makes it possible to release a modified version which carries
* forward this exception.
*/
#include "common.h"
#include "prototypes.h"
/* global OpenSSL initalization: compression, engine, entropy */
static int init_compression(GLOBAL_OPTIONS *);
static int init_prng(GLOBAL_OPTIONS *);
static int add_rand_file(GLOBAL_OPTIONS *, const char *);
int cli_index, opt_index; /* to keep structure for callbacks */
int ssl_init(void) { /* init SSL before parsing configuration file */
SSL_load_error_strings();
SSL_library_init();
cli_index=SSL_get_ex_new_index(0, "cli index", NULL, NULL, NULL);
opt_index=SSL_CTX_get_ex_new_index(0, "opt index", NULL, NULL, NULL);
if(cli_index<0 || opt_index<0)
return 1;
#ifdef HAVE_OSSL_ENGINE_H
ENGINE_load_builtin_engines();
#endif
return 0;
}
int ssl_configure(GLOBAL_OPTIONS *global) { /* configure global SSL settings */
#ifdef USE_FIPS
if(FIPS_mode()!=global->option.fips) {
RAND_set_rand_method(NULL); /* reset RAND methods */
if(!FIPS_mode_set(global->option.fips)) {
ERR_load_crypto_strings();
sslerror("FIPS_mode_set");
return 1;
}
}
s_log(LOG_NOTICE, "FIPS mode is %s",
global->option.fips ? "enabled" : "disabled");
#endif /* USE_FIPS */
if(init_compression(global))
return 1;
if(init_prng(global))
return 1;
s_log(LOG_DEBUG, "PRNG seeded successfully");
return 0; /* SUCCESS */
}
static int init_compression(GLOBAL_OPTIONS *global) {
#ifndef OPENSSL_NO_COMP
SSL_COMP *comp;
STACK_OF(SSL_COMP) *ssl_comp_methods;
ssl_comp_methods=SSL_COMP_get_compression_methods();
if(!ssl_comp_methods) {
if(global->compression==COMP_NONE) {
s_log(LOG_NOTICE, "Failed to get compression methods");
return 0; /* ignore */
} else {
s_log(LOG_ERR, "Failed to get compression methods");
return 1;
}
}
/* delete OpenSSL defaults (empty the SSL_COMP stack) */
/* cannot use sk_SSL_COMP_pop_free, as it also destroys the stack itself */
while(sk_SSL_COMP_num(ssl_comp_methods))
OPENSSL_free(sk_SSL_COMP_pop(ssl_comp_methods));
if(global->compression==COMP_NONE) {
s_log(LOG_DEBUG, "Compression not enabled");
return 0; /* success */
}
/* insert RFC 1951 (DEFLATE) algoritm */
if(SSLeay()>=0x00908051L) { /* 0.9.8e-beta1 */
/* only allow DEFLATE with OpenSSL 0.9.8 or later
with openssl #1468 zlib memory leak fixed */
comp=(SSL_COMP *)OPENSSL_malloc(sizeof(SSL_COMP));
if(!comp) {
s_log(LOG_ERR, "OPENSSL_malloc filed");
return 1;
}
comp->id=1; /* RFC 1951 */
comp->method=COMP_zlib();
if(!comp->method || comp->method->type==NID_undef) {
OPENSSL_free(comp);
s_log(LOG_ERR, "Failed to initialize compression method");
return 1;
}
comp->name=comp->method->name;
sk_SSL_COMP_push(ssl_comp_methods, comp);
}
/* also insert one of obsolete (ZLIB/RLE) algoritms */
comp=(SSL_COMP *)OPENSSL_malloc(sizeof(SSL_COMP));
if(!comp) {
s_log(LOG_ERR, "OPENSSL_malloc filed");
return 1;
}
if(global->compression==COMP_ZLIB) {
comp->id=0xe0; /* 224 - within private range (193 to 255) */
comp->method=COMP_zlib();
} else if(global->compression==COMP_RLE) {
comp->id=0xe1; /* 225 - within private range (193 to 255) */
comp->method=COMP_rle();
} else {
s_log(LOG_INFO, "Compression enabled: %d algorithm(s)",
sk_SSL_COMP_num(ssl_comp_methods));
OPENSSL_free(comp);
return 0;
}
if(!comp->method || comp->method->type==NID_undef) {
OPENSSL_free(comp);
s_log(LOG_ERR, "Failed to initialize compression method");
return 1;
}
comp->name=comp->method->name;
sk_SSL_COMP_push(ssl_comp_methods, comp);
s_log(LOG_INFO, "Compression enabled: %d algorithm(s)",
sk_SSL_COMP_num(ssl_comp_methods));
#endif /* OPENSSL_NO_COMP */
return 0; /* success */
}
static int init_prng(GLOBAL_OPTIONS *global) {
int totbytes=0;
char filename[256];
int bytes;
bytes=0; /* avoid warning if #ifdef'd out for windows */
filename[0]='\0';
/* if they specify a rand file on the command line we
assume that they really do want it, so try it first */
if(global->rand_file) {
totbytes+=add_rand_file(global, global->rand_file);
if(RAND_status())
return 0; /* success */
}
/* try the $RANDFILE or $HOME/.rnd files */
RAND_file_name(filename, 256);
if(filename[0]) {
totbytes+=add_rand_file(global, filename);
if(RAND_status())
return 0; /* success */
}
#ifdef RANDOM_FILE
totbytes+=add_rand_file(global, RANDOM_FILE);
if(RAND_status())
return 0; /* success */
#endif
#ifdef USE_WIN32
RAND_screen();
if(RAND_status()) {
s_log(LOG_DEBUG, "Seeded PRNG with RAND_screen");
return 0; /* success */
}
s_log(LOG_DEBUG, "RAND_screen failed to sufficiently seed PRNG");
#else
if(global->egd_sock) {
if((bytes=RAND_egd(global->egd_sock))==-1) {
s_log(LOG_WARNING, "EGD Socket %s failed", global->egd_sock);
bytes=0;
} else {
totbytes+=bytes;
s_log(LOG_DEBUG, "Snagged %d random bytes from EGD Socket %s",
bytes, global->egd_sock);
return 0; /* OpenSSL always gets what it needs or fails,
so no need to check if seeded sufficiently */
}
}
/* try the good-old default /dev/urandom, if available */
totbytes+=add_rand_file(global, "/dev/urandom");
if(RAND_status())
return 0; /* success */
#endif /* USE_WIN32 */
/* random file specified during configure */
s_log(LOG_ERR, "PRNG seeded with %d bytes total", totbytes);
s_log(LOG_ERR, "PRNG was not seeded with enough random bytes");
return 1; /* FAILED */
}
static int add_rand_file(GLOBAL_OPTIONS *global, const char *filename) {
int readbytes;
int writebytes;
struct stat sb;
if(stat(filename, &sb))
return 0; /* could not stat() file -> return 0 bytes */
if((readbytes=RAND_load_file(filename, global->random_bytes)))
s_log(LOG_DEBUG, "Snagged %d random bytes from %s",
readbytes, filename);
else
s_log(LOG_INFO, "Unable to retrieve any random data from %s",
filename);
/* write new random data for future seeding if it's a regular file */
if(global->option.rand_write && (sb.st_mode & S_IFREG)){
writebytes=RAND_write_file(filename);
if(writebytes==-1)
s_log(LOG_WARNING, "Failed to write strong random data to %s - "
"may be a permissions or seeding problem", filename);
else
s_log(LOG_DEBUG, "Wrote %d new random bytes to %s",
writebytes, filename);
}
return readbytes;
}
/* end of ssl.c */