📄 ssl.c
字号:
/*
* stunnel Universal SSL tunnel
* Copyright (c) 1998-2004 Michal Trojnara <Michal.Trojnara@mirt.net>
* All Rights Reserved
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Michal Trojnara gives
* permission to link the code of this program with the OpenSSL
* library (or with modified versions of OpenSSL that use the same
* license as OpenSSL), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than OpenSSL. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
#ifdef __vms
#include <starlet.h>
#endif /* __vms */
#ifndef NO_RSA
/* Cache temporary keys up to 2048 bits */
#define KEY_CACHE_LENGTH 2049
/* Cache temporary keys up to 1 hour */
#define KEY_CACHE_TIME 3600
#endif /* NO_RSA */
#include "common.h"
#include "prototypes.h"
/* SSL functions */
static int init_dh(void);
static int init_prng(void);
static int prng_seeded(int);
static int add_rand_file(char *);
#ifndef NO_RSA
static RSA *tmp_rsa_cb(SSL *, int, int);
static RSA *make_temp_key(int);
#endif /* NO_RSA */
static void verify_init(void);
static int verify_callback(int, X509_STORE_CTX *);
static int crl_callback(X509_STORE_CTX *);
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
static void info_callback(const SSL *, int, int);
#else
static void info_callback(SSL *, int, int);
#endif
static void print_stats(void);
static void sslerror_stack(void);
SSL_CTX *ctx; /* global SSL context */
static X509_STORE *revocation_store=NULL;
void context_init(void) { /* init SSL */
int i;
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
/* Load all bundled ENGINEs into memory and make them visible */
ENGINE_load_builtin_engines();
/* Register all of them for every algorithm they collectively implement */
ENGINE_register_all_complete();
#endif
if(!init_prng())
log(LOG_INFO, "PRNG seeded successfully");
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
if(options.option.client) {
ctx=SSL_CTX_new(SSLv3_client_method());
} else { /* Server mode */
ctx=SSL_CTX_new(SSLv23_server_method());
#ifndef NO_RSA
SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
#endif /* NO_RSA */
if(init_dh())
log(LOG_WARNING, "Diffie-Hellman initialization failed");
}
if(options.ssl_options) {
log(LOG_DEBUG, "Configuration SSL options: 0x%08lX",
options.ssl_options);
log(LOG_DEBUG, "SSL options set: 0x%08lX",
SSL_CTX_set_options(ctx, options.ssl_options));
}
#if SSLEAY_VERSION_NUMBER >= 0x00906000L
SSL_CTX_set_mode(ctx,
SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif /* OpenSSL-0.9.6 */
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
SSL_CTX_set_timeout(ctx, options.session_timeout);
if(options.option.cert) {
if(!SSL_CTX_use_certificate_chain_file(ctx, options.cert)) {
log(LOG_ERR, "Error reading certificate file: %s", options.cert);
sslerror("SSL_CTX_use_certificate_chain_file");
exit(1);
}
log(LOG_DEBUG, "Certificate: %s", options.cert);
log(LOG_DEBUG, "Key file: %s", options.key);
#ifdef USE_WIN32
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
#endif
for(i=0; i<3; i++) {
#ifdef NO_RSA
if(SSL_CTX_use_PrivateKey_file(ctx, options.key,
SSL_FILETYPE_PEM))
#else /* NO_RSA */
if(SSL_CTX_use_RSAPrivateKey_file(ctx, options.key,
SSL_FILETYPE_PEM))
#endif /* NO_RSA */
break;
if(i<2 && ERR_GET_REASON(ERR_peek_error())==EVP_R_BAD_DECRYPT) {
sslerror_stack(); /* dump the error stack */
log(LOG_ERR, "Wrong pass phrase: retrying");
continue;
}
#ifdef NO_RSA
sslerror("SSL_CTX_use_PrivateKey_file");
#else /* NO_RSA */
sslerror("SSL_CTX_use_RSAPrivateKey_file");
#endif /* NO_RSA */
exit(1);
}
if(!SSL_CTX_check_private_key(ctx)) {
sslerror("Private key does not match the certificate");
exit(1);
}
}
verify_init(); /* Initialize certificate verification */
SSL_CTX_set_info_callback(ctx, info_callback);
if(options.cipher_list) {
if (!SSL_CTX_set_cipher_list(ctx, options.cipher_list)) {
sslerror("SSL_CTX_set_cipher_list");
exit(1);
}
}
}
void context_free(void) { /* free SSL */
SSL_CTX_free(ctx);
}
static int init_prng(void) {
int totbytes=0;
char filename[STRLEN];
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(options.rand_file) {
totbytes+=add_rand_file(options.rand_file);
if(prng_seeded(totbytes))
return 0;
}
/* try the $RANDFILE or $HOME/.rnd files */
RAND_file_name(filename, STRLEN);
if(filename[0]) {
filename[STRLEN-1]='\0'; /* just in case */
totbytes+=add_rand_file(filename);
if(prng_seeded(totbytes))
return 0;
}
#ifdef RANDOM_FILE
totbytes += add_rand_file( RANDOM_FILE );
if(prng_seeded(totbytes))
return 0;
#endif
#ifdef USE_WIN32
RAND_screen();
if(prng_seeded(totbytes)) {
log(LOG_DEBUG, "Seeded PRNG with RAND_screen");
return 0;
}
log(LOG_DEBUG, "RAND_screen failed to sufficiently seed PRNG");
#else
#if SSLEAY_VERSION_NUMBER >= 0x0090581fL
if(options.egd_sock) {
if((bytes=RAND_egd(options.egd_sock))==-1) {
log(LOG_WARNING, "EGD Socket %s failed", options.egd_sock);
bytes=0;
} else {
totbytes += bytes;
log(LOG_DEBUG, "Snagged %d random bytes from EGD Socket %s",
bytes, options.egd_sock);
return 0; /* OpenSSL always gets what it needs or fails,
so no need to check if seeded sufficiently */
}
}
#ifdef EGD_SOCKET
if((bytes=RAND_egd(EGD_SOCKET))==-1) {
log(LOG_WARNING, "EGD Socket %s failed", EGD_SOCKET);
} else {
totbytes += bytes;
log(LOG_DEBUG, "Snagged %d random bytes from EGD Socket %s",
bytes, EGD_SOCKET);
return 0;
}
#endif /* EGD_SOCKET */
#endif /* OpenSSL-0.9.5a */
#endif /* USE_WIN32 */
/* Try the good-old default /dev/urandom, if available */
totbytes+=add_rand_file( "/dev/urandom" );
if(prng_seeded(totbytes))
return 0;
/* Random file specified during configure */
log(LOG_INFO, "PRNG seeded with %d bytes total", totbytes);
log(LOG_WARNING, "PRNG may not have been seeded with enough random bytes");
return -1; /* FAILED */
}
static int init_dh(void) {
#ifdef USE_DH
FILE *fp;
DH *dh;
BIO *bio;
fp=fopen(options.cert, "r");
if(!fp) {
#ifdef USE_WIN32
/* Win32 doesn't seem to set errno in fopen() */
log(LOG_ERR, "Failed to open %s", options.cert);
#else
ioerror(options.cert);
#endif
return -1; /* FAILED */
}
bio=BIO_new_fp(fp, BIO_CLOSE|BIO_FP_TEXT);
if(!bio) {
log(LOG_ERR, "BIO_new_fp failed");
return -1; /* FAILED */
}
if((dh=PEM_read_bio_DHparams(bio, NULL, NULL
#if SSLEAY_VERSION_NUMBER >= 0x00904000L
, NULL
#endif
))) {
BIO_free(bio);
log(LOG_DEBUG, "Using Diffie-Hellman parameters from %s",
options.cert);
} else { /* Failed to load DH parameters from file */
BIO_free(bio);
log(LOG_NOTICE, "Could not load DH parameters from %s", options.cert);
return -1; /* FAILED */
}
SSL_CTX_set_tmp_dh(ctx, dh);
log(LOG_INFO, "Diffie-Hellman initialized with %d bit key",
8*DH_size(dh));
DH_free(dh);
#endif /* USE_DH */
return 0; /* OK */
}
/* shortcut to determine if sufficient entropy for PRNG is present */
static int prng_seeded(int bytes) {
#if SSLEAY_VERSION_NUMBER >= 0x0090581fL
if(RAND_status()){
log(LOG_DEBUG, "RAND_status claims sufficient entropy for the PRNG");
return 1;
}
#else
if(bytes>=options.random_bytes) {
log(LOG_INFO, "Sufficient entropy in PRNG assumed (>= %d)", options.random_bytes);
return 1;
}
#endif
return 0; /* assume we don't have enough */
}
static int add_rand_file(char *filename) {
int readbytes;
int writebytes;
struct stat sb;
if(stat(filename, &sb))
return 0;
if((readbytes=RAND_load_file(filename, options.random_bytes)))
log(LOG_DEBUG, "Snagged %d random bytes from %s", readbytes, filename);
else
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(options.option.rand_write && (sb.st_mode & S_IFREG)){
writebytes = RAND_write_file(filename);
if(writebytes==-1)
log(LOG_WARNING, "Failed to write strong random data to %s - "
"may be a permissions or seeding problem", filename);
else
log(LOG_DEBUG, "Wrote %d new random bytes to %s", writebytes, filename);
}
return readbytes;
}
#ifndef NO_RSA
static RSA *tmp_rsa_cb(SSL *s, int export, int keylen) {
static int initialized=0;
static struct keytabstruct {
RSA *key;
time_t timeout;
} keytable[KEY_CACHE_LENGTH];
static RSA *longkey=NULL;
static int longlen=0;
static time_t longtime=0;
RSA *oldkey, *retval;
time_t now;
int i;
enter_critical_section(CRIT_KEYGEN); /* Only one make_temp_key() at a time */
if(!initialized) {
for(i=0; i<KEY_CACHE_LENGTH; i++) {
keytable[i].key=NULL;
keytable[i].timeout=0;
}
initialized=1;
}
time(&now);
if(keylen<KEY_CACHE_LENGTH) {
if(keytable[keylen].timeout<now) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -