3762 lines
109 KiB
C
3762 lines
109 KiB
C
/*
|
|
* stunnel TLS offloading and load-balancing proxy
|
|
* Copyright (C) 1998-2017 Michal Trojnara <Michal.Trojnara@stunnel.org>
|
|
*
|
|
* 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"
|
|
|
|
#if defined(_WIN32_WCE) && !defined(CONFDIR)
|
|
#define CONFDIR "\\stunnel"
|
|
#endif
|
|
|
|
#define CONFLINELEN (16*1024)
|
|
|
|
typedef enum {
|
|
CMD_BEGIN, /* initialize defaults */
|
|
CMD_EXEC, /* process command */
|
|
CMD_END, /* end of section */
|
|
CMD_FREE, /* TODO: deallocate memory */
|
|
CMD_DEFAULT, /* print default value */
|
|
CMD_HELP /* print help */
|
|
} CMD;
|
|
|
|
NOEXPORT int options_file(char *, CONF_TYPE, SERVICE_OPTIONS **);
|
|
NOEXPORT int options_include(char *, SERVICE_OPTIONS **);
|
|
#ifdef USE_WIN32
|
|
struct dirent {
|
|
char d_name[MAX_PATH];
|
|
};
|
|
int scandir(const char *, struct dirent ***,
|
|
int (*)(const struct dirent *),
|
|
int (*)(const struct dirent **, const struct dirent **));
|
|
int alphasort(const struct dirent **, const struct dirent **);
|
|
#endif
|
|
NOEXPORT char *parse_global_option(CMD, char *, char *);
|
|
NOEXPORT char *parse_service_option(CMD, SERVICE_OPTIONS *, char *, char *);
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
NOEXPORT char *sni_init(SERVICE_OPTIONS *);
|
|
#endif /* !defined(OPENSSL_NO_TLSEXT) */
|
|
|
|
NOEXPORT char *parse_debug_level(char *, SERVICE_OPTIONS *);
|
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
NOEXPORT PSK_KEYS *psk_read(char *);
|
|
NOEXPORT void psk_free(PSK_KEYS *);
|
|
#endif /* !defined(OPENSSL_NO_PSK) */
|
|
|
|
typedef struct {
|
|
char *name;
|
|
long unsigned value;
|
|
} SSL_OPTION;
|
|
|
|
static const SSL_OPTION ssl_opts[] = {
|
|
{"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG},
|
|
{"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG},
|
|
#ifdef SSL_OP_LEGACY_SERVER_CONNECT
|
|
{"LEGACY_SERVER_CONNECT", SSL_OP_LEGACY_SERVER_CONNECT},
|
|
#endif
|
|
{"NETSCAPE_REUSE_CIPHER_CHANGE_BUG",
|
|
SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG},
|
|
#ifdef SSL_OP_TLSEXT_PADDING
|
|
{"TLSEXT_PADDING", SSL_OP_TLSEXT_PADDING},
|
|
#endif
|
|
{"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER},
|
|
#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
|
|
{"SAFARI_ECDHE_ECDSA_BUG", SSL_OP_SAFARI_ECDHE_ECDSA_BUG},
|
|
#endif
|
|
{"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG},
|
|
{"TLS_D5_BUG", SSL_OP_TLS_D5_BUG},
|
|
{"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG},
|
|
#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
|
|
{"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING},
|
|
#endif
|
|
{"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG},
|
|
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
|
|
{"DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS},
|
|
#endif
|
|
{"ALL", SSL_OP_ALL},
|
|
#ifdef SSL_OP_NO_QUERY_MTU
|
|
{"NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU},
|
|
#endif
|
|
#ifdef SSL_OP_COOKIE_EXCHANGE
|
|
{"COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE},
|
|
#endif
|
|
#ifdef SSL_OP_NO_TICKET
|
|
{"NO_TICKET", SSL_OP_NO_TICKET},
|
|
#endif
|
|
#ifdef SSL_OP_CISCO_ANYCONNECT
|
|
{"CISCO_ANYCONNECT", SSL_OP_CISCO_ANYCONNECT},
|
|
#endif
|
|
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
|
|
{"NO_SESSION_RESUMPTION_ON_RENEGOTIATION",
|
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION},
|
|
#endif
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
{"NO_COMPRESSION", SSL_OP_NO_COMPRESSION},
|
|
#endif
|
|
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
|
|
{"ALLOW_UNSAFE_LEGACY_RENEGOTIATION",
|
|
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION},
|
|
#endif
|
|
#ifdef SSL_OP_SINGLE_ECDH_USE
|
|
{"SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE},
|
|
#endif
|
|
{"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE},
|
|
{"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA},
|
|
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
|
|
{"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE},
|
|
#endif
|
|
{"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG},
|
|
{"NO_SSLv2", SSL_OP_NO_SSLv2},
|
|
{"NO_SSLv3", SSL_OP_NO_SSLv3},
|
|
{"NO_TLSv1", SSL_OP_NO_TLSv1},
|
|
#ifdef SSL_OP_NO_TLSv1_1
|
|
{"NO_TLSv1.1", SSL_OP_NO_TLSv1_1},
|
|
#endif
|
|
#ifdef SSL_OP_NO_TLSv1_2
|
|
{"NO_TLSv1.2", SSL_OP_NO_TLSv1_2},
|
|
#endif
|
|
{"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1},
|
|
{"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2},
|
|
{"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG},
|
|
#ifdef SSL_OP_NON_EXPORT_FIRST
|
|
{"NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST},
|
|
#endif
|
|
{"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG},
|
|
#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
|
|
{"CRYPTOPRO_TLSEXT_BUG", SSL_OP_CRYPTOPRO_TLSEXT_BUG},
|
|
#endif
|
|
#ifdef SSL_OP_NO_DTLSv1
|
|
{"NO_DTLSv1", SSL_OP_NO_DTLSv1},
|
|
#endif
|
|
#ifdef SSL_OP_NO_DTLSv1_2
|
|
{"NO_DTLSv1_2", SSL_OP_NO_DTLSv1_2},
|
|
#endif
|
|
#ifdef SSL_OP_NO_SSL_MASK
|
|
{"NO_SSL_MASK", SSL_OP_NO_SSL_MASK},
|
|
#endif
|
|
#ifdef SSL_OP_NO_DTLS_MASK
|
|
{"NO_DTLS_MASK", SSL_OP_NO_DTLS_MASK},
|
|
#endif
|
|
#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
|
|
{"NO_ENCRYPT_THEN_MAC", SSL_OP_NO_ENCRYPT_THEN_MAC},
|
|
#endif
|
|
#ifdef SSL_OP_NO_TLSv1_3
|
|
{"NO_TLSv1_3", SSL_OP_NO_TLSv1_3},
|
|
#endif
|
|
{NULL, 0}
|
|
};
|
|
|
|
NOEXPORT long unsigned parse_ssl_option(char *);
|
|
NOEXPORT void print_ssl_options(void);
|
|
|
|
NOEXPORT void init_socket_options(void);
|
|
NOEXPORT int print_socket_options(void);
|
|
NOEXPORT char *print_option(int, OPT_UNION *);
|
|
NOEXPORT int parse_socket_option(char *);
|
|
|
|
#ifndef OPENSSL_NO_OCSP
|
|
NOEXPORT unsigned long parse_ocsp_flag(char *);
|
|
#endif /* !defined(OPENSSL_NO_OCSP) */
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
NOEXPORT void engine_reset_list(void);
|
|
NOEXPORT char *engine_auto(void);
|
|
NOEXPORT char *engine_open(const char *);
|
|
NOEXPORT char *engine_ctrl(const char *, const char *);
|
|
NOEXPORT char *engine_default(const char *);
|
|
NOEXPORT char *engine_init(void);
|
|
NOEXPORT ENGINE *engine_get_by_id(const char *);
|
|
NOEXPORT ENGINE *engine_get_by_num(const int);
|
|
#endif /* !defined(OPENSSL_NO_ENGINE) */
|
|
|
|
NOEXPORT void print_syntax(void);
|
|
|
|
NOEXPORT void name_list_append(NAME_LIST **, char *);
|
|
#ifndef USE_WIN32
|
|
NOEXPORT char **argalloc(char *);
|
|
#endif
|
|
|
|
char configuration_file[PATH_MAX];
|
|
|
|
GLOBAL_OPTIONS global_options;
|
|
SERVICE_OPTIONS service_options;
|
|
unsigned number_of_sections=0;
|
|
|
|
static GLOBAL_OPTIONS new_global_options;
|
|
static SERVICE_OPTIONS new_service_options;
|
|
|
|
static char *option_not_found=
|
|
"Specified option name is not valid here";
|
|
|
|
static char *stunnel_cipher_list=
|
|
"HIGH:!DH:!aNULL:!SSLv2";
|
|
|
|
/**************************************** parse commandline parameters */
|
|
|
|
/* return values:
|
|
0 - configuration accepted
|
|
1 - error
|
|
2 - information printed
|
|
*/
|
|
|
|
int options_cmdline(char *arg1, char *arg2) {
|
|
char *name;
|
|
CONF_TYPE type;
|
|
|
|
#ifdef USE_WIN32
|
|
(void)arg2; /* squash the unused parameter warning */
|
|
#endif
|
|
if(!arg1) {
|
|
name=
|
|
#ifdef CONFDIR
|
|
CONFDIR
|
|
#ifdef USE_WIN32
|
|
"\\"
|
|
#else
|
|
"/"
|
|
#endif
|
|
#endif
|
|
"stunnel.conf";
|
|
type=CONF_FILE;
|
|
} else if(!strcasecmp(arg1, "-help")) {
|
|
parse_global_option(CMD_HELP, NULL, NULL);
|
|
parse_service_option(CMD_HELP, NULL, NULL, NULL);
|
|
log_flush(LOG_MODE_INFO);
|
|
return 2;
|
|
} else if(!strcasecmp(arg1, "-version")) {
|
|
parse_global_option(CMD_DEFAULT, NULL, NULL);
|
|
parse_service_option(CMD_DEFAULT, NULL, NULL, NULL);
|
|
log_flush(LOG_MODE_INFO);
|
|
return 2;
|
|
} else if(!strcasecmp(arg1, "-sockets")) {
|
|
print_socket_options();
|
|
log_flush(LOG_MODE_INFO);
|
|
return 2;
|
|
} else if(!strcasecmp(arg1, "-options")) {
|
|
print_ssl_options();
|
|
log_flush(LOG_MODE_INFO);
|
|
return 2;
|
|
} else
|
|
#ifndef USE_WIN32
|
|
if(!strcasecmp(arg1, "-fd")) {
|
|
if(!arg2) {
|
|
s_log(LOG_ERR, "No file descriptor specified");
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
name=arg2;
|
|
type=CONF_FD;
|
|
} else
|
|
#endif
|
|
{
|
|
name=arg1;
|
|
type=CONF_FILE;
|
|
}
|
|
|
|
#ifdef HAVE_REALPATH
|
|
if(type==CONF_FILE) {
|
|
if(!realpath(name, configuration_file)) {
|
|
s_log(LOG_ERR, "Invalid configuration file name \"%s\"", name);
|
|
ioerror("realpath");
|
|
return 1;
|
|
}
|
|
return options_parse(type);
|
|
}
|
|
#endif
|
|
strncpy(configuration_file, name, PATH_MAX-1);
|
|
configuration_file[PATH_MAX-1]='\0';
|
|
return options_parse(type);
|
|
}
|
|
|
|
/**************************************** parse configuration file */
|
|
|
|
int options_parse(CONF_TYPE type) {
|
|
SERVICE_OPTIONS *section;
|
|
char *errstr;
|
|
|
|
options_defaults();
|
|
section=&new_service_options;
|
|
if(options_file(configuration_file, type, §ion))
|
|
return 1;
|
|
|
|
if(new_service_options.next) { /* daemon mode: initialize sections */
|
|
for(section=new_service_options.next; section; section=section->next) {
|
|
s_log(LOG_INFO, "Initializing service [%s]", section->servname);
|
|
errstr=parse_service_option(CMD_END, section, NULL, NULL);
|
|
if(errstr)
|
|
break;
|
|
}
|
|
} else { /* inetd mode: need to initialize global options */
|
|
errstr=parse_global_option(CMD_END, NULL, NULL);
|
|
if(errstr) {
|
|
s_log(LOG_ERR, "Global options: %s", errstr);
|
|
return 1;
|
|
}
|
|
s_log(LOG_INFO, "Initializing inetd mode configuration");
|
|
section=&new_service_options;
|
|
errstr=parse_service_option(CMD_END, section, NULL, NULL);
|
|
}
|
|
if(errstr) {
|
|
s_log(LOG_ERR, "Service [%s]: %s", section->servname, errstr);
|
|
return 1;
|
|
}
|
|
|
|
s_log(LOG_NOTICE, "Configuration successful");
|
|
return 0;
|
|
}
|
|
|
|
NOEXPORT int options_file(char *path, CONF_TYPE type, SERVICE_OPTIONS **section) {
|
|
DISK_FILE *df;
|
|
char line_text[CONFLINELEN], *errstr;
|
|
char config_line[CONFLINELEN], *config_opt, *config_arg;
|
|
int i, line_number=0;
|
|
#ifndef USE_WIN32
|
|
int fd;
|
|
char *tmp_str;
|
|
#endif
|
|
|
|
s_log(LOG_NOTICE, "Reading configuration from %s %s",
|
|
type==CONF_FD ? "descriptor" : "file", path);
|
|
#ifndef USE_WIN32
|
|
if(type==CONF_FD) { /* file descriptor */
|
|
fd=(int)strtol(path, &tmp_str, 10);
|
|
if(tmp_str==path || *tmp_str) { /* not a number */
|
|
s_log(LOG_ERR, "Invalid file descriptor number");
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
df=file_fdopen(fd);
|
|
} else
|
|
#endif
|
|
df=file_open(path, FILE_MODE_READ);
|
|
if(!df) {
|
|
s_log(LOG_ERR, "Cannot open configuration file");
|
|
if(type!=CONF_RELOAD)
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
|
|
while(file_getline(df, line_text, CONFLINELEN)>=0) {
|
|
memcpy(config_line, line_text, CONFLINELEN);
|
|
++line_number;
|
|
config_opt=config_line;
|
|
if(line_number==1) {
|
|
if(config_opt[0]==(char)0xef &&
|
|
config_opt[1]==(char)0xbb &&
|
|
config_opt[2]==(char)0xbf) {
|
|
s_log(LOG_NOTICE, "UTF-8 byte order mark detected");
|
|
config_opt+=3;
|
|
} else {
|
|
s_log(LOG_NOTICE, "UTF-8 byte order mark not detected");
|
|
}
|
|
}
|
|
|
|
while(isspace((unsigned char)*config_opt))
|
|
++config_opt; /* remove initial whitespaces */
|
|
for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i)
|
|
config_opt[i]='\0'; /* remove trailing whitespaces */
|
|
if(config_opt[0]=='\0' || config_opt[0]=='#' || config_opt[0]==';') /* empty or comment */
|
|
continue;
|
|
|
|
if(config_opt[0]=='[' && config_opt[strlen(config_opt)-1]==']') { /* new section */
|
|
SERVICE_OPTIONS *new_section;
|
|
|
|
if(!new_service_options.next) { /* initialize global options */
|
|
errstr=parse_global_option(CMD_END, NULL, NULL);
|
|
if(errstr) {
|
|
s_log(LOG_ERR, "%s:%d: \"%s\": %s",
|
|
path, line_number, line_text, errstr);
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
}
|
|
++config_opt;
|
|
config_opt[strlen(config_opt)-1]='\0';
|
|
new_section=str_alloc(sizeof(SERVICE_OPTIONS));
|
|
memcpy(new_section, &new_service_options, sizeof(SERVICE_OPTIONS));
|
|
new_section->servname=str_dup(config_opt);
|
|
new_section->session=NULL;
|
|
new_section->next=NULL;
|
|
(*section)->next=new_section;
|
|
*section=new_section;
|
|
continue;
|
|
}
|
|
|
|
config_arg=strchr(config_line, '=');
|
|
if(!config_arg) {
|
|
s_log(LOG_ERR, "%s:%d: \"%s\": No '=' found",
|
|
path, line_number, line_text);
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
*config_arg++='\0'; /* split into option name and argument value */
|
|
for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i)
|
|
config_opt[i]='\0'; /* remove trailing whitespaces */
|
|
while(isspace((unsigned char)*config_arg))
|
|
++config_arg; /* remove initial whitespaces */
|
|
|
|
if(!strcasecmp(config_opt, "include")) {
|
|
if(options_include(config_arg, section)) {
|
|
s_log(LOG_ERR, "%s:%d: Failed to include directory \"%s\"",
|
|
path, line_number, config_arg);
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
errstr=option_not_found;
|
|
/* try global options first (e.g. for 'debug') */
|
|
if(!new_service_options.next)
|
|
errstr=parse_global_option(CMD_EXEC, config_opt, config_arg);
|
|
if(errstr==option_not_found)
|
|
errstr=parse_service_option(CMD_EXEC, *section, config_opt, config_arg);
|
|
if(errstr) {
|
|
s_log(LOG_ERR, "%s:%d: \"%s\": %s",
|
|
path, line_number, line_text, errstr);
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
}
|
|
file_close(df);
|
|
return 0;
|
|
}
|
|
|
|
NOEXPORT int options_include(char *directory, SERVICE_OPTIONS **section) {
|
|
struct dirent **namelist;
|
|
int i, num, err=0;
|
|
|
|
num=scandir(directory, &namelist, NULL, alphasort);
|
|
if(num<0) {
|
|
ioerror("scandir");
|
|
return 1;
|
|
}
|
|
for(i=0; i<num; ++i) {
|
|
if(!err) {
|
|
struct stat sb;
|
|
char *name=str_printf(
|
|
#ifdef USE_WIN32
|
|
"%s\\%s",
|
|
#else
|
|
"%s/%s",
|
|
#endif
|
|
directory, namelist[i]->d_name);
|
|
stat(name, &sb);
|
|
if(S_ISREG(sb.st_mode))
|
|
err=options_file(name, CONF_FILE, section);
|
|
else
|
|
s_log(LOG_DEBUG, "\"%s\" is not a file", name);
|
|
str_free(name);
|
|
}
|
|
free(namelist[i]);
|
|
}
|
|
free(namelist);
|
|
return err;
|
|
}
|
|
|
|
#ifdef USE_WIN32
|
|
|
|
int scandir(const char *dirp, struct dirent ***namelist,
|
|
int (*filter)(const struct dirent *),
|
|
int (*compar)(const struct dirent **, const struct dirent **)) {
|
|
WIN32_FIND_DATA data;
|
|
HANDLE h;
|
|
unsigned num=0, allocated=0;
|
|
LPTSTR path, pattern;
|
|
char *name;
|
|
DWORD saved_errno;
|
|
|
|
(void)filter; /* squash the unused parameter warning */
|
|
(void)compar; /* squash the unused parameter warning */
|
|
path=str2tstr(dirp);
|
|
pattern=str_tprintf(TEXT("%s\\*"), path);
|
|
str_free(path);
|
|
h=FindFirstFile(pattern, &data);
|
|
saved_errno=GetLastError();
|
|
str_free(pattern);
|
|
SetLastError(saved_errno);
|
|
if(h==INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
*namelist=NULL;
|
|
do {
|
|
if(num>=allocated) {
|
|
allocated+=16;
|
|
*namelist=realloc(*namelist, allocated*sizeof(**namelist));
|
|
}
|
|
(*namelist)[num]=malloc(sizeof(struct dirent));
|
|
if(!(*namelist)[num])
|
|
return -1;
|
|
name=tstr2str(data.cFileName);
|
|
strncpy((*namelist)[num]->d_name, name, MAX_PATH-1);
|
|
(*namelist)[num]->d_name[MAX_PATH-1]='\0';
|
|
str_free(name);
|
|
++num;
|
|
} while(FindNextFile(h, &data));
|
|
FindClose(h);
|
|
return (int)num;
|
|
}
|
|
|
|
int alphasort(const struct dirent **a, const struct dirent **b) {
|
|
(void)a; /* squash the unused parameter warning */
|
|
(void)b; /* squash the unused parameter warning */
|
|
/* most Windows filesystem return sorted data */
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
void options_defaults() {
|
|
/* initialize globals *before* opening the config file */
|
|
memset(&new_global_options, 0, sizeof(GLOBAL_OPTIONS)); /* reset global options */
|
|
memset(&new_service_options, 0, sizeof(SERVICE_OPTIONS)); /* reset local options */
|
|
new_service_options.next=NULL;
|
|
parse_global_option(CMD_BEGIN, NULL, NULL);
|
|
parse_service_option(CMD_BEGIN, &new_service_options, NULL, NULL);
|
|
}
|
|
|
|
void options_apply() { /* apply default/validated configuration */
|
|
unsigned num=0;
|
|
SERVICE_OPTIONS *section;
|
|
|
|
for(section=new_service_options.next; section; section=section->next)
|
|
section->section_number=num++;
|
|
/* FIXME: this operation may be unsafe, as client() threads use it */
|
|
memcpy(&global_options, &new_global_options, sizeof(GLOBAL_OPTIONS));
|
|
/* service_options are used for inetd mode and to enumerate services */
|
|
memcpy(&service_options, &new_service_options, sizeof(SERVICE_OPTIONS));
|
|
number_of_sections=num;
|
|
}
|
|
|
|
/**************************************** global options */
|
|
|
|
NOEXPORT char *parse_global_option(CMD cmd, char *opt, char *arg) {
|
|
if(cmd==CMD_DEFAULT || cmd==CMD_HELP) {
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Global options:");
|
|
}
|
|
|
|
/* chroot */
|
|
#ifdef HAVE_CHROOT
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.chroot_dir=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "chroot"))
|
|
break;
|
|
new_global_options.chroot_dir=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = directory to chroot stunnel process", "chroot");
|
|
break;
|
|
}
|
|
#endif /* HAVE_CHROOT */
|
|
|
|
/* compression */
|
|
#ifndef OPENSSL_NO_COMP
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.compression=COMP_NONE;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "compression"))
|
|
break;
|
|
if(OpenSSL_version_num()>=0x00908051L && !strcasecmp(arg, "deflate"))
|
|
new_global_options.compression=COMP_DEFLATE;
|
|
else if(!strcasecmp(arg, "zlib"))
|
|
new_global_options.compression=COMP_ZLIB;
|
|
else
|
|
return "Specified compression type is not available";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = compression type",
|
|
"compression");
|
|
break;
|
|
}
|
|
#endif /* !defined(OPENSSL_NO_COMP) */
|
|
|
|
/* EGD */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
#ifdef EGD_SOCKET
|
|
new_global_options.egd_sock=EGD_SOCKET;
|
|
#else
|
|
new_global_options.egd_sock=NULL;
|
|
#endif
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "EGD"))
|
|
break;
|
|
new_global_options.egd_sock=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#ifdef EGD_SOCKET
|
|
s_log(LOG_NOTICE, "%-22s = %s", "EGD", EGD_SOCKET);
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = path to Entropy Gathering Daemon socket", "EGD");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
/* engine */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
engine_reset_list();
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engine"))
|
|
break;
|
|
if(!strcasecmp(arg, "auto"))
|
|
return engine_auto();
|
|
else
|
|
return engine_open(arg);
|
|
case CMD_END:
|
|
engine_init();
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = auto|engine_id",
|
|
"engine");
|
|
break;
|
|
}
|
|
|
|
/* engineCtrl */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineCtrl"))
|
|
break;
|
|
{
|
|
char *tmp_str=strchr(arg, ':');
|
|
if(tmp_str)
|
|
*tmp_str++='\0';
|
|
return engine_ctrl(arg, tmp_str);
|
|
}
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = cmd[:arg]",
|
|
"engineCtrl");
|
|
break;
|
|
}
|
|
|
|
/* engineDefault */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineDefault"))
|
|
break;
|
|
return engine_default(arg);
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = TASK_LIST",
|
|
"engineDefault");
|
|
break;
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_ENGINE) */
|
|
|
|
/* fips */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
#ifdef USE_FIPS
|
|
new_global_options.option.fips=0;
|
|
#endif /* USE_FIPS */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "fips"))
|
|
break;
|
|
#ifdef USE_FIPS
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.fips=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.fips=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
#else
|
|
if(strcasecmp(arg, "no"))
|
|
return "FIPS support is not available";
|
|
#endif /* USE_FIPS */
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
#ifdef USE_FIPS
|
|
s_log(LOG_NOTICE, "%-22s = yes|no FIPS 140-2 mode",
|
|
"fips");
|
|
#endif /* USE_FIPS */
|
|
break;
|
|
}
|
|
|
|
/* foreground */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.option.foreground=0;
|
|
new_global_options.option.log_stderr=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "foreground"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes")) {
|
|
new_global_options.option.foreground=1;
|
|
new_global_options.option.log_stderr=1;
|
|
} else if(!strcasecmp(arg, "quiet")) {
|
|
new_global_options.option.foreground=1;
|
|
new_global_options.option.log_stderr=0;
|
|
} else if(!strcasecmp(arg, "no")) {
|
|
new_global_options.option.foreground=0;
|
|
new_global_options.option.log_stderr=0;
|
|
} else
|
|
return "The argument needs to be either 'yes', 'quiet' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|quiet|no foreground mode (don't fork, log to stderr)",
|
|
"foreground");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ICON_IMAGE
|
|
|
|
/* iconActive */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.icon[ICON_ACTIVE]=load_icon_default(ICON_ACTIVE);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "iconActive"))
|
|
break;
|
|
if(!(new_global_options.icon[ICON_ACTIVE]=load_icon_file(arg)))
|
|
return "Failed to load the specified icon";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = icon when connections are established", "iconActive");
|
|
break;
|
|
}
|
|
|
|
/* iconError */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.icon[ICON_ERROR]=load_icon_default(ICON_ERROR);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "iconError"))
|
|
break;
|
|
if(!(new_global_options.icon[ICON_ERROR]=load_icon_file(arg)))
|
|
return "Failed to load the specified icon";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = icon for invalid configuration file", "iconError");
|
|
break;
|
|
}
|
|
|
|
/* iconIdle */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.icon[ICON_IDLE]=load_icon_default(ICON_IDLE);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "iconIdle"))
|
|
break;
|
|
if(!(new_global_options.icon[ICON_IDLE]=load_icon_file(arg)))
|
|
return "Failed to load the specified icon";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = icon when no connections were established", "iconIdle");
|
|
break;
|
|
}
|
|
|
|
#endif /* ICON_IMAGE */
|
|
|
|
/* log */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.log_file_mode=FILE_MODE_APPEND;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "log"))
|
|
break;
|
|
if(!strcasecmp(arg, "append"))
|
|
new_global_options.log_file_mode=FILE_MODE_APPEND;
|
|
else if(!strcasecmp(arg, "overwrite"))
|
|
new_global_options.log_file_mode=FILE_MODE_OVERWRITE;
|
|
else
|
|
return "The argument needs to be either 'append' or 'overwrite'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = append|overwrite log file",
|
|
"log");
|
|
break;
|
|
}
|
|
|
|
/* output */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.output_file=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "output"))
|
|
break;
|
|
new_global_options.output_file=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = file to append log messages", "output");
|
|
break;
|
|
}
|
|
|
|
/* pid */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.pidfile=NULL; /* do not create a pid file */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "pid"))
|
|
break;
|
|
if(arg[0]) /* is argument not empty? */
|
|
new_global_options.pidfile=str_dup(arg);
|
|
else
|
|
new_global_options.pidfile=NULL; /* empty -> do not create a pid file */
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = pid file", "pid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* RNDbytes */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.random_bytes=RANDOM_BYTES;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "RNDbytes"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
new_global_options.random_bytes=(long)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal number of bytes to read from random seed files";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d", "RNDbytes", RANDOM_BYTES);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = bytes to read from random seed files", "RNDbytes");
|
|
break;
|
|
}
|
|
|
|
/* RNDfile */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.rand_file=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "RNDfile"))
|
|
break;
|
|
new_global_options.rand_file=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#ifdef RANDOM_FILE
|
|
s_log(LOG_NOTICE, "%-22s = %s", "RNDfile", RANDOM_FILE);
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = path to file with random seed data", "RNDfile");
|
|
break;
|
|
}
|
|
|
|
/* RNDoverwrite */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.option.rand_write=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "RNDoverwrite"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.rand_write=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.rand_write=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = yes", "RNDoverwrite");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no overwrite seed datafiles with new random data",
|
|
"RNDoverwrite");
|
|
break;
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
/* service */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_service_options.servname=str_dup("stunnel");
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "service"))
|
|
break;
|
|
new_service_options.servname=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = service name", "service");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* socket */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
init_socket_options();
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "socket"))
|
|
break;
|
|
if(parse_socket_option(arg))
|
|
return "Illegal socket option";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = a|l|r:option=value[:value]", "socket");
|
|
s_log(LOG_NOTICE, "%25sset an option on accept/local/remote socket", "");
|
|
break;
|
|
}
|
|
|
|
/* syslog */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.option.log_syslog=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "syslog"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.log_syslog=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.log_syslog=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no send logging messages to syslog",
|
|
"syslog");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* taskbar */
|
|
#ifdef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
new_global_options.option.taskbar=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "taskbar"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.taskbar=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.taskbar=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = yes", "taskbar");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no enable the taskbar icon", "taskbar");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if(cmd==CMD_EXEC)
|
|
return option_not_found;
|
|
|
|
if(cmd==CMD_END) {
|
|
/* FIPS needs to be initialized as early as possible */
|
|
if(ssl_configure(&new_global_options)) /* configure global TLS settings */
|
|
return "Failed to initialize TLS";
|
|
}
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
/**************************************** service-level options */
|
|
|
|
NOEXPORT char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section,
|
|
char *opt, char *arg) {
|
|
int endpoints=0;
|
|
#ifndef USE_WIN32
|
|
struct group *gr;
|
|
struct passwd *pw;
|
|
#endif
|
|
|
|
if(cmd==CMD_DEFAULT || cmd==CMD_HELP) {
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Service-level options:");
|
|
}
|
|
|
|
/* accept */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
addrlist_clear(§ion->local_addr, 1);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "accept"))
|
|
break;
|
|
section->option.accept=1;
|
|
name_list_append(§ion->local_addr.names, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->local_addr.names) {
|
|
if(!addrlist_resolve(§ion->local_addr))
|
|
return "Cannot resolve accept target";
|
|
++endpoints;
|
|
}
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = [host:]port accept connections on specified host:port",
|
|
"accept");
|
|
break;
|
|
}
|
|
|
|
/* CApath */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
#if 0
|
|
section->ca_dir=(char *)X509_get_default_cert_dir();
|
|
#endif
|
|
section->ca_dir=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "CApath"))
|
|
break;
|
|
if(arg[0]) /* not empty */
|
|
section->ca_dir=str_dup(arg);
|
|
else
|
|
section->ca_dir=NULL;
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#if 0
|
|
s_log(LOG_NOTICE, "%-22s = %s", "CApath",
|
|
section->ca_dir ? section->ca_dir : "(none)");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = CA certificate directory for 'verify' option",
|
|
"CApath");
|
|
break;
|
|
}
|
|
|
|
/* CAfile */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
#if 0
|
|
section->ca_file=(char *)X509_get_default_certfile();
|
|
#endif
|
|
section->ca_file=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "CAfile"))
|
|
break;
|
|
if(arg[0]) /* not empty */
|
|
section->ca_file=str_dup(arg);
|
|
else
|
|
section->ca_file=NULL;
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#if 0
|
|
s_log(LOG_NOTICE, "%-22s = %s", "CAfile",
|
|
section->ca_file ? section->ca_file : "(none)");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = CA certificate file for 'verify' option",
|
|
"CAfile");
|
|
break;
|
|
}
|
|
|
|
/* cert */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->cert=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "cert"))
|
|
break;
|
|
section->cert=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
#ifndef OPENSSL_NO_PSK
|
|
if(section->psk_keys)
|
|
break;
|
|
#endif /* !defined(OPENSSL_NO_PSK) */
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
if(section->engine)
|
|
break;
|
|
#endif /* !defined(OPENSSL_NO_ENGINE) */
|
|
if(!section->option.client && !section->cert)
|
|
return "TLS server needs a certificate";
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break; /* no default certificate */
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = certificate chain", "cert");
|
|
break;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER>=0x10002000L
|
|
|
|
/* checkEmail */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->check_email=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "checkEmail"))
|
|
break;
|
|
name_list_append(§ion->check_email, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->check_email && !section->option.verify_chain && !section->option.verify_peer)
|
|
return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled";
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = peer certificate email address",
|
|
"checkEmail");
|
|
break;
|
|
}
|
|
|
|
/* checkHost */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->check_host=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "checkHost"))
|
|
break;
|
|
name_list_append(§ion->check_host, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->check_host && !section->option.verify_chain && !section->option.verify_peer)
|
|
return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled";
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = peer certificate host name pattern",
|
|
"checkHost");
|
|
break;
|
|
}
|
|
|
|
/* checkIP */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->check_ip=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "checkIP"))
|
|
break;
|
|
name_list_append(§ion->check_ip, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = peer certificate IP address",
|
|
"checkIP");
|
|
break;
|
|
}
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */
|
|
|
|
/* ciphers */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->cipher_list=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ciphers"))
|
|
break;
|
|
section->cipher_list=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(!section->cipher_list) {
|
|
/* this is only executed for global options,
|
|
* because section->cipher_list is no longer NULL */
|
|
#ifdef USE_FIPS
|
|
if(new_global_options.option.fips)
|
|
section->cipher_list="FIPS";
|
|
else
|
|
#endif /* USE_FIPS */
|
|
section->cipher_list=stunnel_cipher_list;
|
|
}
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#ifdef USE_FIPS
|
|
s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers",
|
|
"FIPS", "(with \"fips = yes\")");
|
|
s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers",
|
|
stunnel_cipher_list, "(with \"fips = no\")");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-22s = %s", "ciphers", stunnel_cipher_list);
|
|
#endif /* USE_FIPS */
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = list of permitted TLS ciphers", "ciphers");
|
|
break;
|
|
}
|
|
|
|
/* client */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.client=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "client"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.client=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.client=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no client mode (remote service uses TLS)",
|
|
"client");
|
|
break;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER>=0x10002000L
|
|
|
|
/* config */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->config=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "config"))
|
|
break;
|
|
name_list_append(§ion->config, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = command[:parameter] to execute",
|
|
"config");
|
|
break;
|
|
}
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */
|
|
|
|
/* connect */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
addrlist_clear(§ion->connect_addr, 0);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "connect"))
|
|
break;
|
|
name_list_append(§ion->connect_addr.names, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->connect_addr.names) {
|
|
if(!section->option.delayed_lookup &&
|
|
!addrlist_resolve(§ion->connect_addr)) {
|
|
s_log(LOG_INFO,
|
|
"Cannot resolve connect target - delaying DNS lookup");
|
|
section->redirect_addr.num=0;
|
|
str_free(section->redirect_addr.names);
|
|
section->redirect_addr.names=NULL;
|
|
section->option.delayed_lookup=1;
|
|
}
|
|
++endpoints;
|
|
}
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = [host:]port to connect",
|
|
"connect");
|
|
break;
|
|
}
|
|
|
|
/* CRLpath */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->crl_dir=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "CRLpath"))
|
|
break;
|
|
if(arg[0]) /* not empty */
|
|
section->crl_dir=str_dup(arg);
|
|
else
|
|
section->crl_dir=NULL;
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = CRL directory", "CRLpath");
|
|
break;
|
|
}
|
|
|
|
/* CRLfile */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->crl_file=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "CRLfile"))
|
|
break;
|
|
if(arg[0]) /* not empty */
|
|
section->crl_file=str_dup(arg);
|
|
else
|
|
section->crl_file=NULL;
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = CRL file", "CRLfile");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
/* curve */
|
|
#define DEFAULT_CURVE NID_X9_62_prime256v1
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->curve=DEFAULT_CURVE;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "curve"))
|
|
break;
|
|
section->curve=OBJ_txt2nid(arg);
|
|
if(section->curve==NID_undef)
|
|
return "Curve name not supported";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %s", "curve", OBJ_nid2ln(DEFAULT_CURVE));
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = ECDH curve name", "curve");
|
|
break;
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_ECDH) */
|
|
|
|
/* debug */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->log_level=LOG_NOTICE;
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
new_global_options.log_facility=LOG_DAEMON;
|
|
#endif
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "debug"))
|
|
break;
|
|
return parse_debug_level(arg, section);
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
s_log(LOG_NOTICE, "%-22s = %s", "debug", "daemon.notice");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-22s = %s", "debug", "notice");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
s_log(LOG_NOTICE, "%-22s = [facility].level (e.g. daemon.info)", "debug");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-22s = level (e.g. info)", "debug");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/* delay */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.delayed_lookup=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "delay"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.delayed_lookup=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.delayed_lookup=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = yes|no delay DNS lookup for 'connect' option",
|
|
"delay");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
/* engineId */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineId"))
|
|
break;
|
|
section->engine=engine_get_by_id(arg);
|
|
if(!section->engine)
|
|
return "Engine ID not found";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = ID of engine to read the key from",
|
|
"engineId");
|
|
break;
|
|
}
|
|
|
|
/* engineNum */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineNum"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
int tmp_int=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal engine number";
|
|
section->engine=engine_get_by_num(tmp_int-1);
|
|
}
|
|
if(!section->engine)
|
|
return "Illegal engine number";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = number of engine to read the key from",
|
|
"engineNum");
|
|
break;
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_ENGINE) */
|
|
|
|
/* exec */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->exec_name=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "exec"))
|
|
break;
|
|
section->exec_name=str_dup(arg);
|
|
#ifdef USE_WIN32
|
|
section->exec_args=str_dup(arg);
|
|
#else
|
|
if(!section->exec_args) {
|
|
section->exec_args=str_alloc(2*sizeof(char *));
|
|
section->exec_args[0]=section->exec_name;
|
|
section->exec_args[1]=NULL; /* to show that it's null-terminated */
|
|
}
|
|
#endif
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->exec_name)
|
|
++endpoints;
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = file execute local inetd-type program",
|
|
"exec");
|
|
break;
|
|
}
|
|
|
|
/* execArgs */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->exec_args=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "execArgs"))
|
|
break;
|
|
#ifdef USE_WIN32
|
|
section->exec_args=str_dup(arg);
|
|
#else
|
|
section->exec_args=argalloc(arg);
|
|
#endif
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = arguments for 'exec' (including $0)",
|
|
"execArgs");
|
|
break;
|
|
}
|
|
|
|
/* failover */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->failover=FAILOVER_RR;
|
|
section->seq=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "failover"))
|
|
break;
|
|
if(!strcasecmp(arg, "rr"))
|
|
section->failover=FAILOVER_RR;
|
|
else if(!strcasecmp(arg, "prio"))
|
|
section->failover=FAILOVER_PRIO;
|
|
else
|
|
return "The argument needs to be either 'rr' or 'prio'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->option.delayed_lookup)
|
|
section->failover=FAILOVER_PRIO;
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = rr|prio failover strategy",
|
|
"failover");
|
|
break;
|
|
}
|
|
|
|
/* ident */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->username=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ident"))
|
|
break;
|
|
section->username=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = username for IDENT (RFC 1413) checking", "ident");
|
|
break;
|
|
}
|
|
|
|
/* key */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->key=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "key"))
|
|
break;
|
|
section->key=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->cert && !section->key)
|
|
section->key=str_dup(section->cert);
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = certificate private key", "key");
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_LIBWRAP
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.libwrap=0; /* disable libwrap by default */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "libwrap"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.libwrap=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.libwrap=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no use /etc/hosts.allow and /etc/hosts.deny",
|
|
"libwrap");
|
|
break;
|
|
}
|
|
#endif /* USE_LIBWRAP */
|
|
|
|
/* local */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.local=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "local"))
|
|
break;
|
|
if(!hostport2addr(§ion->source_addr, arg, "0", 1))
|
|
return "Failed to resolve local address";
|
|
section->option.local=1;
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = IP address to be used as source for remote"
|
|
" connections", "local");
|
|
break;
|
|
}
|
|
|
|
/* logId */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->log_id=LOG_ID_SEQUENTIAL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "logId"))
|
|
break;
|
|
if(!strcasecmp(arg, "sequential"))
|
|
section->log_id=LOG_ID_SEQUENTIAL;
|
|
else if(!strcasecmp(arg, "unique"))
|
|
section->log_id=LOG_ID_UNIQUE;
|
|
else if(!strcasecmp(arg, "thread"))
|
|
section->log_id=LOG_ID_THREAD;
|
|
else if(!strcasecmp(arg, "process"))
|
|
section->log_id=LOG_ID_PROCESS;
|
|
else
|
|
return "Invalid connection identifier type";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %s", "logId", "sequential");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = connection identifier type",
|
|
"logId");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_OCSP
|
|
|
|
/* OCSP */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->ocsp_url=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ocsp"))
|
|
break;
|
|
section->ocsp_url=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = OCSP responder URL", "ocsp");
|
|
break;
|
|
}
|
|
|
|
/* OCSPaia */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.aia=0; /* disable AIA by default */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "OCSPaia"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.aia=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.aia=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = yes|no check the AIA responders from certificates",
|
|
"OCSPaia");
|
|
break;
|
|
}
|
|
|
|
/* OCSPflag */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->ocsp_flags=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "OCSPflag"))
|
|
break;
|
|
{
|
|
unsigned long tmp_ulong=parse_ocsp_flag(arg);
|
|
if(!tmp_ulong)
|
|
return "Illegal OCSP flag";
|
|
section->ocsp_flags|=tmp_ulong;
|
|
}
|
|
return NULL;
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = OCSP responder flags", "OCSPflag");
|
|
break;
|
|
}
|
|
|
|
/* OCSPnonce */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.nonce=0; /* disable OCSP nonce by default */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "OCSPnonce"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.nonce=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.nonce=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = yes|no send and verify the OCSP nonce extension",
|
|
"OCSPnonce");
|
|
break;
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_OCSP) */
|
|
|
|
/* options */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->ssl_options_set|=SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
|
|
#if OPENSSL_VERSION_NUMBER>=0x009080dfL
|
|
section->ssl_options_clear=0;
|
|
#endif /* OpenSSL 0.9.8m or later */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "options"))
|
|
break;
|
|
#if OPENSSL_VERSION_NUMBER>=0x009080dfL
|
|
if(*arg=='-') {
|
|
long unsigned tmp=parse_ssl_option(arg+1);
|
|
if(!tmp)
|
|
return "Illegal TLS option";
|
|
section->ssl_options_clear|=tmp;
|
|
return NULL; /* OK */
|
|
}
|
|
#endif /* OpenSSL 0.9.8m or later */
|
|
{
|
|
long unsigned tmp=parse_ssl_option(arg);
|
|
if(!tmp)
|
|
return "Illegal TLS option";
|
|
section->ssl_options_set|=tmp;
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv2");
|
|
s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv3");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = TLS option to set/reset", "options");
|
|
break;
|
|
}
|
|
|
|
/* protocol */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocol"))
|
|
break;
|
|
section->protocol=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
/* PROTOCOL_CHECK also initializes:
|
|
section->option.connect_before_ssl
|
|
section->option.protocol_endpoint */
|
|
{
|
|
char *tmp_str=protocol(NULL, section, PROTOCOL_CHECK);
|
|
if(tmp_str)
|
|
return tmp_str;
|
|
}
|
|
endpoints+=section->option.protocol_endpoint;
|
|
#ifdef SSL_OP_NO_TICKET
|
|
/* disable RFC4507 support introduced in OpenSSL 0.9.8f */
|
|
/* session tickets do not support SSL_SESSION_*_ex_data() */
|
|
if(!section->option.connect_before_ssl) /* address cache can be used */
|
|
section->ssl_options_set|=SSL_OP_NO_TICKET;
|
|
#endif
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = protocol to negotiate before TLS initialization",
|
|
"protocol");
|
|
s_log(LOG_NOTICE, "%25scurrently supported: cifs, connect, imap,", "");
|
|
s_log(LOG_NOTICE, "%25s nntp, pgsql, pop3, proxy, smtp, socks", "");
|
|
break;
|
|
}
|
|
|
|
/* protocolAuthentication */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol_authentication="basic";
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolAuthentication"))
|
|
break;
|
|
section->protocol_authentication=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = authentication type for protocol negotiations",
|
|
"protocolAuthentication");
|
|
break;
|
|
}
|
|
|
|
/* protocolDomain */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol_domain=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolDomain"))
|
|
break;
|
|
section->protocol_domain=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = domain for protocol negotiations",
|
|
"protocolDomain");
|
|
break;
|
|
}
|
|
|
|
/* protocolHost */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol_host=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolHost"))
|
|
break;
|
|
section->protocol_host=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = host:port for protocol negotiations",
|
|
"protocolHost");
|
|
break;
|
|
}
|
|
|
|
/* protocolPassword */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol_password=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolPassword"))
|
|
break;
|
|
section->protocol_password=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = password for protocol negotiations",
|
|
"protocolPassword");
|
|
break;
|
|
}
|
|
|
|
/* protocolUsername */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->protocol_username=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolUsername"))
|
|
break;
|
|
section->protocol_username=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = username for protocol negotiations",
|
|
"protocolUsername");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
|
|
/* PSKidentity */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->psk_identity=NULL;
|
|
section->psk_selected=NULL;
|
|
section->psk_sorted.val=NULL;
|
|
section->psk_sorted.num=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "PSKidentity"))
|
|
break;
|
|
section->psk_identity=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(!section->psk_keys) /* PSK not configured */
|
|
break;
|
|
psk_sort(§ion->psk_sorted, section->psk_keys);
|
|
if(section->option.client) {
|
|
if(section->psk_identity) {
|
|
section->psk_selected=
|
|
psk_find(§ion->psk_sorted, section->psk_identity);
|
|
if(!section->psk_selected)
|
|
return "No key found for the specified PSK identity";
|
|
} else { /* take the first specified identity as default */
|
|
section->psk_selected=section->psk_keys;
|
|
}
|
|
} else {
|
|
if(section->psk_identity)
|
|
s_log(LOG_NOTICE,
|
|
"PSK identity is ignored in the server mode");
|
|
}
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = identity for PSK authentication",
|
|
"PSKidentity");
|
|
break;
|
|
}
|
|
|
|
/* PSKsecrets */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->psk_keys=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "PSKsecrets"))
|
|
break;
|
|
section->psk_keys=psk_read(arg);
|
|
if(!section->psk_keys)
|
|
return "Failed to read PSK secrets";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
psk_free(section->psk_keys);
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = secrets for PSK authentication",
|
|
"PSKsecrets");
|
|
break;
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_PSK) */
|
|
|
|
/* pty */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.pty=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "pty"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.pty=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.pty=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no allocate pseudo terminal for 'exec' option",
|
|
"pty");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* redirect */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
addrlist_clear(§ion->redirect_addr, 0);
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "redirect"))
|
|
break;
|
|
#ifdef SSL_OP_NO_TICKET
|
|
/* disable RFC4507 support introduced in OpenSSL 0.9.8f */
|
|
/* session tickets do not support SSL_SESSION_*_ex_data() */
|
|
section->ssl_options_set|=SSL_OP_NO_TICKET;
|
|
#endif
|
|
name_list_append(§ion->redirect_addr.names, arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->redirect_addr.names) {
|
|
if(!section->option.delayed_lookup &&
|
|
!addrlist_resolve(§ion->redirect_addr)) {
|
|
s_log(LOG_INFO,
|
|
"Cannot resolve redirect target - delaying DNS lookup");
|
|
section->connect_addr.num=0;
|
|
str_free(section->connect_addr.names);
|
|
section->connect_addr.names=NULL;
|
|
section->option.delayed_lookup=1;
|
|
}
|
|
if(!section->option.verify_chain && !section->option.verify_peer)
|
|
return "Either \"verifyChain\" or \"verifyPeer\" has to be enabled for \"redirect\" to work";
|
|
}
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = [host:]port to redirect on authentication failures",
|
|
"redirect");
|
|
break;
|
|
}
|
|
|
|
/* renegotiation */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.renegotiation=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "renegotiation"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.renegotiation=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.renegotiation=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no support renegotiation",
|
|
"renegotiation");
|
|
break;
|
|
}
|
|
|
|
/* requireCert */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.require_cert=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "requireCert"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes")) {
|
|
section->option.request_cert=1;
|
|
section->option.require_cert=1;
|
|
} else if(!strcasecmp(arg, "no")) {
|
|
section->option.require_cert=0;
|
|
} else {
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no require client certificate",
|
|
"requireCert");
|
|
break;
|
|
}
|
|
|
|
/* reset */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.reset=1; /* enabled by default */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "reset"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.reset=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.reset=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no send TCP RST on error",
|
|
"retry");
|
|
break;
|
|
}
|
|
|
|
/* retry */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.retry=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "retry"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
section->option.retry=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
section->option.retry=0;
|
|
else
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no retry connect+exec section",
|
|
"retry");
|
|
break;
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
/* setgid */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->gid=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "setgid"))
|
|
break;
|
|
gr=getgrnam(arg);
|
|
if(gr) {
|
|
section->gid=gr->gr_gid;
|
|
return NULL; /* OK */
|
|
}
|
|
{
|
|
char *tmp_str;
|
|
section->gid=(gid_t)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal GID";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = groupname for setgid()", "setgid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifndef USE_WIN32
|
|
/* setuid */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->uid=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "setuid"))
|
|
break;
|
|
pw=getpwnam(arg);
|
|
if(pw) {
|
|
section->uid=pw->pw_uid;
|
|
return NULL; /* OK */
|
|
}
|
|
{
|
|
char *tmp_str;
|
|
section->uid=(uid_t)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal UID";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = username for setuid()", "setuid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* sessionCacheSize */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->session_size=1000L;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sessionCacheSize"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->session_size=strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal session cache size";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %ld", "sessionCacheSize", 1000L);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = session cache size", "sessionCacheSize");
|
|
break;
|
|
}
|
|
|
|
/* sessionCacheTimeout */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->session_timeout=300L;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sessionCacheTimeout") && strcasecmp(opt, "session"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->session_timeout=strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal session cache timeout";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %ld seconds", "sessionCacheTimeout", 300L);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = session cache timeout (in seconds)",
|
|
"sessionCacheTimeout");
|
|
break;
|
|
}
|
|
|
|
/* sessiond */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.sessiond=0;
|
|
memset(§ion->sessiond_addr, 0, sizeof(SOCKADDR_UNION));
|
|
section->sessiond_addr.in.sin_family=AF_INET;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sessiond"))
|
|
break;
|
|
section->option.sessiond=1;
|
|
#ifdef SSL_OP_NO_TICKET
|
|
/* disable RFC4507 support introduced in OpenSSL 0.9.8f */
|
|
/* this prevents session callbacks from being executed */
|
|
section->ssl_options_set|=SSL_OP_NO_TICKET;
|
|
#endif
|
|
if(!name2addr(§ion->sessiond_addr, arg, 0))
|
|
return "Failed to resolve sessiond server address";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = [host:]port use sessiond at host:port",
|
|
"sessiond");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
/* sni */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->servername_list_head=NULL;
|
|
section->servername_list_tail=NULL;
|
|
section->option.sni=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sni"))
|
|
break;
|
|
section->sni=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
{
|
|
char *tmp_str=sni_init(section);
|
|
if(tmp_str)
|
|
return tmp_str;
|
|
}
|
|
if(section->option.sni)
|
|
++endpoints;
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = master_service:host_name for an SNI virtual service",
|
|
"sni");
|
|
break;
|
|
}
|
|
#endif /* !defined(OPENSSL_NO_TLSEXT) */
|
|
|
|
/* sslVersion */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
#if OPENSSL_VERSION_NUMBER>=0x10100000L
|
|
section->client_method=(SSL_METHOD *)TLS_client_method();
|
|
section->server_method=(SSL_METHOD *)TLS_server_method();
|
|
#else
|
|
section->client_method=(SSL_METHOD *)SSLv23_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv23_server_method();
|
|
#endif
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sslVersion"))
|
|
break;
|
|
if(!strcasecmp(arg, "all")) {
|
|
#if OPENSSL_VERSION_NUMBER>=0x10100000L
|
|
section->client_method=(SSL_METHOD *)TLS_client_method();
|
|
section->server_method=(SSL_METHOD *)TLS_server_method();
|
|
#else
|
|
section->client_method=(SSL_METHOD *)SSLv23_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv23_server_method();
|
|
#endif
|
|
#if OPENSSL_API_COMPAT<0x10100000L
|
|
} else if(!strcasecmp(arg, "SSLv2")) {
|
|
#ifndef OPENSSL_NO_SSL2
|
|
section->client_method=(SSL_METHOD *)SSLv2_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv2_server_method();
|
|
#else /* defined(OPENSSL_NO_SSL2) */
|
|
return "SSLv2 not supported";
|
|
#endif /* !defined(OPENSSL_NO_SSL2) */
|
|
} else if(!strcasecmp(arg, "SSLv3")) {
|
|
#ifndef OPENSSL_NO_SSL3
|
|
section->client_method=(SSL_METHOD *)SSLv3_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv3_server_method();
|
|
#else /* defined(OPENSSL_NO_SSL3) */
|
|
return "SSLv3 not supported";
|
|
#endif /* !defined(OPENSSL_NO_SSL3) */
|
|
} else if(!strcasecmp(arg, "TLSv1")) {
|
|
#ifndef OPENSSL_NO_TLS1
|
|
section->client_method=(SSL_METHOD *)TLSv1_client_method();
|
|
section->server_method=(SSL_METHOD *)TLSv1_server_method();
|
|
#else /* defined(OPENSSL_NO_TLS1) */
|
|
return "TLSv1 not supported";
|
|
#endif /* !defined(OPENSSL_NO_TLS1) */
|
|
} else if(!strcasecmp(arg, "TLSv1.1")) {
|
|
#ifndef OPENSSL_NO_TLS1_1
|
|
section->client_method=(SSL_METHOD *)TLSv1_1_client_method();
|
|
section->server_method=(SSL_METHOD *)TLSv1_1_server_method();
|
|
#else /* defined(OPENSSL_NO_TLS1_1) */
|
|
return "TLSv1.1 not supported";
|
|
#endif /* !defined(OPENSSL_NO_TLS1_1) */
|
|
} else if(!strcasecmp(arg, "TLSv1.2")) {
|
|
#ifndef OPENSSL_NO_TLS1_2
|
|
section->client_method=(SSL_METHOD *)TLSv1_2_client_method();
|
|
section->server_method=(SSL_METHOD *)TLSv1_2_server_method();
|
|
#else /* defined(OPENSSL_NO_TLS1_2) */
|
|
return "TLSv1.2 not supported";
|
|
#endif /* !defined(OPENSSL_NO_TLS1_2) */
|
|
#endif /* OPENSSL_API_COMPAT<0x10100000L */
|
|
} else
|
|
return "Incorrect version of TLS protocol";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
#ifdef USE_FIPS
|
|
if(new_global_options.option.fips) {
|
|
#ifndef OPENSSL_NO_SSL2
|
|
if(section->option.client ?
|
|
section->client_method==(SSL_METHOD *)SSLv2_client_method() :
|
|
section->server_method==(SSL_METHOD *)SSLv2_server_method())
|
|
return "\"sslVersion = SSLv2\" not supported in FIPS mode";
|
|
#endif /* !defined(OPENSSL_NO_SSL2) */
|
|
#ifndef OPENSSL_NO_SSL3
|
|
if(section->option.client ?
|
|
section->client_method==(SSL_METHOD *)SSLv3_client_method() :
|
|
section->server_method==(SSL_METHOD *)SSLv3_server_method())
|
|
return "\"sslVersion = SSLv3\" not supported in FIPS mode";
|
|
#endif /* !defined(OPENSSL_NO_SSL3) */
|
|
}
|
|
#endif /* USE_FIPS */
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = all"
|
|
#if OPENSSL_VERSION_NUMBER<0x10100000L
|
|
"|SSLv2|SSLv3|TLSv1"
|
|
#if OPENSSL_VERSION_NUMBER>=0x10001000L
|
|
"|TLSv1.1|TLSv1.2"
|
|
#endif /* OPENSSL_VERSION_NUMBER>=0x10001000L */
|
|
#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */
|
|
" TLS method", "sslVersion");
|
|
break;
|
|
}
|
|
|
|
#ifndef USE_FORK
|
|
/* stack */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->stack_size=DEFAULT_STACK_SIZE;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "stack"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->stack_size=(size_t)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal thread stack size";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d bytes", "stack", DEFAULT_STACK_SIZE);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = thread stack size (in bytes)", "stack");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* TIMEOUTbusy */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->timeout_busy=300; /* 5 minutes */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTbusy"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->timeout_busy=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal busy timeout";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTbusy", 300);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = seconds to wait for expected data", "TIMEOUTbusy");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTclose */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->timeout_close=60; /* 1 minute */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTclose"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->timeout_close=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal close timeout";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTclose", 60);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = seconds to wait for close_notify",
|
|
"TIMEOUTclose");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTconnect */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->timeout_connect=10; /* 10 seconds */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTconnect"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->timeout_connect=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal connect timeout";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTconnect", 10);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = seconds to connect remote host", "TIMEOUTconnect");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTidle */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->timeout_idle=43200; /* 12 hours */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTidle"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
section->timeout_idle=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return "Illegal idle timeout";
|
|
return NULL; /* OK */
|
|
}
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTidle", 43200);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = seconds to keep an idle connection", "TIMEOUTidle");
|
|
break;
|
|
}
|
|
|
|
/* transparent */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.transparent_src=0;
|
|
section->option.transparent_dst=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "transparent"))
|
|
break;
|
|
if(!strcasecmp(arg, "none") || !strcasecmp(arg, "no")) {
|
|
section->option.transparent_src=0;
|
|
section->option.transparent_dst=0;
|
|
} else if(!strcasecmp(arg, "source") || !strcasecmp(arg, "yes")) {
|
|
section->option.transparent_src=1;
|
|
section->option.transparent_dst=0;
|
|
} else if(!strcasecmp(arg, "destination")) {
|
|
section->option.transparent_src=0;
|
|
section->option.transparent_dst=1;
|
|
} else if(!strcasecmp(arg, "both")) {
|
|
section->option.transparent_src=1;
|
|
section->option.transparent_dst=1;
|
|
} else
|
|
return "Selected transparent proxy mode is not available";
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if(section->option.transparent_dst)
|
|
++endpoints;
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = none|source|destination|both transparent proxy mode",
|
|
"transparent");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* verify */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.request_cert=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "verify"))
|
|
break;
|
|
{
|
|
char *tmp_str;
|
|
int tmp_int=(int)strtol(arg, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str || tmp_int<0 || tmp_int>4)
|
|
return "Bad verify level";
|
|
section->option.request_cert=1;
|
|
section->option.require_cert=(tmp_int>=2);
|
|
section->option.verify_chain=(tmp_int>=1 && tmp_int<=3);
|
|
section->option.verify_peer=(tmp_int>=3);
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
if((section->option.verify_chain || section->option.verify_peer) &&
|
|
!section->ca_file && !section->ca_dir)
|
|
return "Either \"CAfile\" or \"CApath\" has to be configured";
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-22s = none", "verify");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-22s = level of peer certificate verification", "verify");
|
|
s_log(LOG_NOTICE,
|
|
"%25slevel 0 - request and ignore peer cert", "");
|
|
s_log(LOG_NOTICE,
|
|
"%25slevel 1 - only validate peer cert if present", "");
|
|
s_log(LOG_NOTICE,
|
|
"%25slevel 2 - always require a valid peer cert", "");
|
|
s_log(LOG_NOTICE,
|
|
"%25slevel 3 - verify peer with locally installed cert", "");
|
|
s_log(LOG_NOTICE,
|
|
"%25slevel 4 - ignore CA chain and only verify peer cert", "");
|
|
break;
|
|
}
|
|
|
|
/* verifyChain */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.verify_chain=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "verifyChain"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes")) {
|
|
section->option.request_cert=1;
|
|
section->option.require_cert=1;
|
|
section->option.verify_chain=1;
|
|
} else if(!strcasecmp(arg, "no")) {
|
|
section->option.verify_chain=0;
|
|
} else {
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no verify certificate chain",
|
|
"verifyChain");
|
|
break;
|
|
}
|
|
|
|
/* verifyPeer */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
section->option.verify_peer=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "verifyPeer"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes")) {
|
|
section->option.request_cert=1;
|
|
section->option.require_cert=1;
|
|
section->option.verify_peer=1;
|
|
} else if(!strcasecmp(arg, "no")) {
|
|
section->option.verify_peer=0;
|
|
} else {
|
|
return "The argument needs to be either 'yes' or 'no'";
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_END:
|
|
break;
|
|
case CMD_FREE:
|
|
break;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-22s = yes|no verify peer certificate",
|
|
"verifyPeer");
|
|
break;
|
|
}
|
|
|
|
/* final checks */
|
|
switch(cmd) {
|
|
case CMD_BEGIN:
|
|
break;
|
|
case CMD_EXEC:
|
|
return option_not_found;
|
|
case CMD_END:
|
|
if(new_service_options.next) { /* daemon mode checks */
|
|
if(endpoints!=2)
|
|
return "Each service must define two endpoints";
|
|
} else { /* inetd mode checks */
|
|
if(section->option.accept)
|
|
return "'accept' option is only allowed in a [section]";
|
|
/* no need to check for section->option.sni in inetd mode,
|
|
as it requires valid sections to be set */
|
|
if(endpoints!=1)
|
|
return "Inetd mode must define one endpoint";
|
|
}
|
|
if(context_init(section)) /* initialize TLS context */
|
|
return "Failed to initialize TLS context";
|
|
case CMD_FREE:
|
|
case CMD_DEFAULT:
|
|
case CMD_HELP:
|
|
break;
|
|
}
|
|
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
/**************************************** validate and initialize configuration */
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
NOEXPORT char *sni_init(SERVICE_OPTIONS *section) {
|
|
char *tmp_str;
|
|
SERVICE_OPTIONS *tmpsrv;
|
|
|
|
/* server mode: update servername_list based on the SNI option */
|
|
if(!section->option.client && section->sni) {
|
|
tmp_str=strchr(section->sni, ':');
|
|
if(!tmp_str)
|
|
return "Invalid SNI parameter format";
|
|
*tmp_str++='\0';
|
|
for(tmpsrv=new_service_options.next; tmpsrv; tmpsrv=tmpsrv->next)
|
|
if(!strcmp(tmpsrv->servname, section->sni))
|
|
break;
|
|
if(!tmpsrv)
|
|
return "SNI section name not found";
|
|
if(tmpsrv->option.client)
|
|
return "SNI master service is a TLS client";
|
|
if(tmpsrv->servername_list_tail) {
|
|
tmpsrv->servername_list_tail->next=str_alloc(sizeof(SERVERNAME_LIST));
|
|
tmpsrv->servername_list_tail=tmpsrv->servername_list_tail->next;
|
|
} else { /* first virtual service */
|
|
tmpsrv->servername_list_head=
|
|
tmpsrv->servername_list_tail=
|
|
str_alloc(sizeof(SERVERNAME_LIST));
|
|
tmpsrv->ssl_options_set|=
|
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
|
|
}
|
|
tmpsrv->servername_list_tail->servername=str_dup(tmp_str);
|
|
tmpsrv->servername_list_tail->opt=section;
|
|
tmpsrv->servername_list_tail->next=NULL;
|
|
section->option.sni=1;
|
|
/* always negotiate a new session on renegotiation, as the TLS
|
|
* context settings (including access control) may be different */
|
|
section->ssl_options_set|=
|
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
|
|
}
|
|
|
|
/* client mode: setup SNI default based on 'protocolHost' and 'connect' options */
|
|
if(section->option.client && !section->sni) {
|
|
/* setup host_name for SNI, prefer SNI and protocolHost if specified */
|
|
if(section->protocol_host) /* 'protocolHost' option */
|
|
section->sni=str_dup(section->protocol_host);
|
|
else if(section->connect_addr.names) /* 'connect' option */
|
|
section->sni=str_dup(section->connect_addr.names->name); /* first hostname */
|
|
if(section->sni) { /* either 'protocolHost' or 'connect' specified */
|
|
tmp_str=strrchr(section->sni, ':');
|
|
if(tmp_str) { /* 'host:port' -> drop ':port' */
|
|
*tmp_str='\0';
|
|
} else { /* 'port' -> default to 'localhost' */
|
|
str_free(section->sni);
|
|
section->sni=str_dup("localhost");
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif /* !defined(OPENSSL_NO_TLSEXT) */
|
|
|
|
/**************************************** facility/debug level */
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int value;
|
|
} facilitylevel;
|
|
|
|
NOEXPORT char *parse_debug_level(char *arg, SERVICE_OPTIONS *section) {
|
|
char *arg_copy;
|
|
char *string;
|
|
facilitylevel *fl;
|
|
|
|
/* facilities only make sense on unix */
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
facilitylevel facilities[] = {
|
|
{"auth", LOG_AUTH}, {"cron", LOG_CRON}, {"daemon", LOG_DAEMON},
|
|
{"kern", LOG_KERN}, {"lpr", LOG_LPR}, {"mail", LOG_MAIL},
|
|
{"news", LOG_NEWS}, {"syslog", LOG_SYSLOG}, {"user", LOG_USER},
|
|
{"uucp", LOG_UUCP}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1},
|
|
{"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4},
|
|
{"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7},
|
|
|
|
/* some facilities are not defined on all Unices */
|
|
#ifdef LOG_AUTHPRIV
|
|
{"authpriv", LOG_AUTHPRIV},
|
|
#endif
|
|
#ifdef LOG_FTP
|
|
{"ftp", LOG_FTP},
|
|
#endif
|
|
#ifdef LOG_NTP
|
|
{"ntp", LOG_NTP},
|
|
#endif
|
|
{NULL, 0}
|
|
};
|
|
#endif /* USE_WIN32, __vms */
|
|
|
|
facilitylevel levels[] = {
|
|
{"emerg", LOG_EMERG}, {"alert", LOG_ALERT},
|
|
{"crit", LOG_CRIT}, {"err", LOG_ERR},
|
|
{"warning", LOG_WARNING}, {"notice", LOG_NOTICE},
|
|
{"info", LOG_INFO}, {"debug", LOG_DEBUG},
|
|
{NULL, -1}
|
|
};
|
|
|
|
arg_copy=str_dup(arg);
|
|
string=arg_copy;
|
|
|
|
/* facilities only make sense on Unix */
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
if(section==&new_service_options && strchr(string, '.')) {
|
|
/* a facility was specified in the global options */
|
|
new_global_options.log_facility=-1;
|
|
string=strtok(arg_copy, "."); /* break it up */
|
|
|
|
for(fl=facilities; fl->name; ++fl) {
|
|
if(!strcasecmp(fl->name, string)) {
|
|
new_global_options.log_facility=fl->value;
|
|
break;
|
|
}
|
|
}
|
|
if(new_global_options.log_facility==-1)
|
|
return "Illegal syslog facility";
|
|
string=strtok(NULL, "."); /* set to the remainder */
|
|
}
|
|
#endif /* USE_WIN32, __vms */
|
|
|
|
/* time to check the syslog level */
|
|
if(string && strlen(string)==1 && *string>='0' && *string<='7') {
|
|
section->log_level=*string-'0';
|
|
return NULL; /* OK */
|
|
}
|
|
section->log_level=8; /* illegal level */
|
|
for(fl=levels; fl->name; ++fl) {
|
|
if(!strcasecmp(fl->name, string)) {
|
|
section->log_level=fl->value;
|
|
break;
|
|
}
|
|
}
|
|
if(section->log_level==8)
|
|
return "Illegal debug level"; /* FAILED */
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
/**************************************** TLS options */
|
|
|
|
NOEXPORT long unsigned parse_ssl_option(char *arg) {
|
|
SSL_OPTION *option;
|
|
|
|
for(option=(SSL_OPTION *)ssl_opts; option->name; ++option)
|
|
if(!strcasecmp(option->name, arg))
|
|
return option->value;
|
|
return 0; /* FAILED */
|
|
}
|
|
|
|
NOEXPORT void print_ssl_options(void) {
|
|
SSL_OPTION *option;
|
|
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Supported TLS options:");
|
|
for(option=(SSL_OPTION *)ssl_opts; option->name; ++option)
|
|
s_log(LOG_NOTICE, "options = %s", option->name);
|
|
}
|
|
|
|
/**************************************** read PSK file */
|
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
|
|
NOEXPORT PSK_KEYS *psk_read(char *key_file) {
|
|
DISK_FILE *df;
|
|
char line[CONFLINELEN], *key_val;
|
|
size_t key_len;
|
|
PSK_KEYS *head=NULL, *tail=NULL, *curr;
|
|
int line_number=0;
|
|
|
|
if(file_permissions(key_file))
|
|
return NULL;
|
|
df=file_open(key_file, FILE_MODE_READ);
|
|
if(!df) {
|
|
s_log(LOG_ERR, "Cannot open PSKsecrets file");
|
|
return NULL;
|
|
}
|
|
while(file_getline(df, line, CONFLINELEN)>=0) {
|
|
++line_number;
|
|
if(!line[0]) /* empty line */
|
|
continue;
|
|
key_val=strchr(line, ':');
|
|
if(!key_val) {
|
|
s_log(LOG_ERR,
|
|
"PSKsecrets line %d: Not in identity:key format",
|
|
line_number);
|
|
file_close(df);
|
|
psk_free(head);
|
|
return NULL;
|
|
}
|
|
*key_val++='\0';
|
|
key_len=strlen(key_val);
|
|
if(strlen(line)+1>PSK_MAX_IDENTITY_LEN) { /* with the trailing '\0' */
|
|
s_log(LOG_ERR,
|
|
"PSKsecrets line %d: Identity longer than %d characters",
|
|
line_number, PSK_MAX_IDENTITY_LEN);
|
|
file_close(df);
|
|
psk_free(head);
|
|
return NULL;
|
|
}
|
|
if(key_len>PSK_MAX_PSK_LEN) {
|
|
s_log(LOG_ERR,
|
|
"PSKsecrets line %d: Key longer than %d characters",
|
|
line_number, PSK_MAX_PSK_LEN);
|
|
file_close(df);
|
|
psk_free(head);
|
|
return NULL;
|
|
}
|
|
if(key_len<20) {
|
|
/* shorter keys are unlikely to have sufficient entropy */
|
|
s_log(LOG_ERR,
|
|
"PSKsecrets line %d: Key shorter than 20 characters",
|
|
line_number);
|
|
file_close(df);
|
|
psk_free(head);
|
|
return NULL;
|
|
}
|
|
curr=str_alloc(sizeof(PSK_KEYS));
|
|
curr->identity=str_dup(line);
|
|
curr->key_val=(unsigned char *)str_dup(key_val);
|
|
curr->key_len=key_len;
|
|
curr->next=NULL;
|
|
if(head)
|
|
tail->next=curr;
|
|
else
|
|
head=curr;
|
|
tail=curr;
|
|
}
|
|
file_close(df);
|
|
return head;
|
|
}
|
|
|
|
NOEXPORT void psk_free(PSK_KEYS *head) {
|
|
PSK_KEYS *next;
|
|
|
|
while(head) {
|
|
next=head->next;
|
|
str_free(head->identity);
|
|
str_free(head->key_val);
|
|
str_free(head);
|
|
head=next;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/**************************************** socket options */
|
|
|
|
static int on=1;
|
|
#define DEF_ON ((void *)&on)
|
|
|
|
SOCK_OPT *sock_opts=NULL, sock_opts_def[]= {
|
|
{"SO_DEBUG", SOL_SOCKET, SO_DEBUG, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
{"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
{"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
{"SO_LINGER", SOL_SOCKET, SO_LINGER, TYPE_LINGER, {NULL, NULL, NULL}},
|
|
{"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
{"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, TYPE_INT, {NULL, NULL, NULL}},
|
|
{"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, TYPE_INT, {NULL, NULL, NULL}},
|
|
#ifdef SO_RCVLOWAT
|
|
{"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef SO_SNDLOWAT
|
|
{"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef SO_RCVTIMEO
|
|
{"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, TYPE_TIMEVAL, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef SO_SNDTIMEO
|
|
{"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, TYPE_TIMEVAL, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef USE_WIN32
|
|
{"SO_EXCLUSIVEADDRUSE", SOL_SOCKET, SO_EXCLUSIVEADDRUSE, TYPE_FLAG, {DEF_ON, NULL, NULL}},
|
|
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
#else
|
|
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, {DEF_ON, NULL, NULL}},
|
|
#endif
|
|
#ifdef SO_BINDTODEVICE
|
|
{"SO_BINDTODEVICE", SOL_SOCKET, SO_BINDTODEVICE, TYPE_STRING, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef SOL_TCP
|
|
#ifdef TCP_KEEPCNT
|
|
{"TCP_KEEPCNT", SOL_TCP, TCP_KEEPCNT, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef TCP_KEEPIDLE
|
|
{"TCP_KEEPIDLE", SOL_TCP, TCP_KEEPIDLE, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef TCP_KEEPINTVL
|
|
{"TCP_KEEPINTVL", SOL_TCP, TCP_KEEPINTVL, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#endif /* SOL_TCP */
|
|
#ifdef IP_TOS
|
|
{"IP_TOS", IPPROTO_IP, IP_TOS, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef IP_TTL
|
|
{"IP_TTL", IPPROTO_IP, IP_TTL, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef IP_MAXSEG
|
|
{"TCP_MAXSEG", IPPROTO_TCP, TCP_MAXSEG, TYPE_INT, {NULL, NULL, NULL}},
|
|
#endif
|
|
{"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, TYPE_FLAG, {NULL, DEF_ON, DEF_ON}},
|
|
#ifdef IP_FREEBIND
|
|
{"IP_FREEBIND", IPPROTO_IP, IP_FREEBIND, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef IP_BINDANY
|
|
{"IP_BINDANY", IPPROTO_IP, IP_BINDANY, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef IPV6_BINDANY
|
|
{"IPV6_BINDANY", IPPROTO_IPV6, IPV6_BINDANY, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
#endif
|
|
#ifdef IPV6_V6ONLY
|
|
{"IPV6_V6ONLY", IPPROTO_IPV6, IPV6_V6ONLY, TYPE_FLAG, {NULL, NULL, NULL}},
|
|
#endif
|
|
{NULL, 0, 0, TYPE_NONE, {NULL, NULL, NULL}}
|
|
};
|
|
|
|
NOEXPORT void init_socket_options(void) {
|
|
#ifdef USE_WIN32
|
|
DWORD version;
|
|
int major, minor;
|
|
SOCK_OPT *ptr;
|
|
|
|
version=GetVersion();
|
|
major=LOBYTE(LOWORD(version));
|
|
minor=HIBYTE(LOWORD(version));
|
|
s_log(LOG_DEBUG, "Running on Windows %d.%d", major, minor);
|
|
|
|
for(ptr=sock_opts_def; ptr->opt_str; ++ptr)
|
|
if(ptr->opt_level==SOL_SOCKET && ptr->opt_name==SO_EXCLUSIVEADDRUSE)
|
|
ptr->opt_val[0]=major>5 ? DEF_ON : NULL; /* Vista or later */
|
|
#endif
|
|
|
|
if(!sock_opts)
|
|
sock_opts=str_alloc_detached(sizeof sock_opts_def);
|
|
memcpy(sock_opts, sock_opts_def, sizeof sock_opts_def);
|
|
}
|
|
|
|
NOEXPORT int print_socket_options(void) {
|
|
SOCKET fd;
|
|
socklen_t optlen;
|
|
SOCK_OPT *ptr;
|
|
OPT_UNION val;
|
|
char *ta, *tl, *tr, *td;
|
|
|
|
fd=socket(AF_INET, SOCK_STREAM, 0);
|
|
init_socket_options();
|
|
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Socket option defaults:");
|
|
s_log(LOG_NOTICE,
|
|
" Option Name | Accept | Local | Remote |OS default");
|
|
s_log(LOG_NOTICE,
|
|
" --------------------+----------+----------+----------+----------");
|
|
for(ptr=sock_opts; ptr->opt_str; ++ptr) {
|
|
/* get OS default value */
|
|
optlen=sizeof val;
|
|
if(getsockopt(fd, ptr->opt_level,
|
|
ptr->opt_name, (void *)&val, &optlen)) {
|
|
switch(get_last_socket_error()) {
|
|
case S_ENOPROTOOPT:
|
|
case S_EOPNOTSUPP:
|
|
td=str_dup("write-only");
|
|
break;
|
|
default:
|
|
s_log(LOG_ERR, "Failed to get %s OS default", ptr->opt_str);
|
|
sockerror("getsockopt");
|
|
closesocket(fd);
|
|
return 1; /* FAILED */
|
|
}
|
|
} else
|
|
td=print_option(ptr->opt_type, &val);
|
|
/* get stunnel default values */
|
|
ta=print_option(ptr->opt_type, ptr->opt_val[0]);
|
|
tl=print_option(ptr->opt_type, ptr->opt_val[1]);
|
|
tr=print_option(ptr->opt_type, ptr->opt_val[2]);
|
|
/* print collected data and fee the memory */
|
|
s_log(LOG_NOTICE, " %-20s|%10s|%10s|%10s|%10s",
|
|
ptr->opt_str, ta, tl, tr, td);
|
|
str_free(ta); str_free(tl); str_free(tr); str_free(td);
|
|
}
|
|
closesocket(fd);
|
|
return 0; /* OK */
|
|
}
|
|
|
|
NOEXPORT char *print_option(int type, OPT_UNION *val) {
|
|
if(!val)
|
|
return str_dup(" -- ");
|
|
switch(type) {
|
|
case TYPE_FLAG:
|
|
return str_printf("%s", val->i_val ? "yes" : "no");
|
|
case TYPE_INT:
|
|
return str_printf("%d", val->i_val);
|
|
case TYPE_LINGER:
|
|
return str_printf("%d:%d",
|
|
val->linger_val.l_onoff, val->linger_val.l_linger);
|
|
case TYPE_TIMEVAL:
|
|
return str_printf("%d:%d",
|
|
(int)val->timeval_val.tv_sec, (int)val->timeval_val.tv_usec);
|
|
case TYPE_STRING:
|
|
return str_printf("%s", val->c_val);
|
|
}
|
|
return str_dup(" Ooops? "); /* internal error? */
|
|
}
|
|
|
|
NOEXPORT int parse_socket_option(char *arg) {
|
|
int socket_type; /* 0-accept, 1-local, 2-remote */
|
|
char *opt_val_str, *opt_val2_str, *tmp_str;
|
|
SOCK_OPT *ptr;
|
|
|
|
if(arg[1]!=':')
|
|
return 1; /* FAILED */
|
|
switch(arg[0]) {
|
|
case 'a':
|
|
socket_type=0; break;
|
|
case 'l':
|
|
socket_type=1; break;
|
|
case 'r':
|
|
socket_type=2; break;
|
|
default:
|
|
return 1; /* FAILED */
|
|
}
|
|
arg+=2;
|
|
opt_val_str=strchr(arg, '=');
|
|
if(!opt_val_str) /* no '='? */
|
|
return 1; /* FAILED */
|
|
*opt_val_str++='\0';
|
|
ptr=sock_opts;
|
|
for(;;) {
|
|
if(!ptr->opt_str)
|
|
return 1; /* FAILED */
|
|
if(!strcmp(arg, ptr->opt_str))
|
|
break; /* option name found */
|
|
++ptr;
|
|
}
|
|
ptr->opt_val[socket_type]=str_alloc(sizeof(OPT_UNION));
|
|
switch(ptr->opt_type) {
|
|
case TYPE_FLAG:
|
|
if(!strcasecmp(opt_val_str, "yes") || !strcmp(opt_val_str, "1")) {
|
|
ptr->opt_val[socket_type]->i_val=1;
|
|
return 0; /* OK */
|
|
}
|
|
if(!strcasecmp(opt_val_str, "no") || !strcmp(opt_val_str, "0")) {
|
|
ptr->opt_val[socket_type]->i_val=0;
|
|
return 0; /* OK */
|
|
}
|
|
return 1; /* FAILED */
|
|
case TYPE_INT:
|
|
ptr->opt_val[socket_type]->i_val=(int)strtol(opt_val_str, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return 1; /* FAILED */
|
|
return 0; /* OK */
|
|
case TYPE_LINGER:
|
|
opt_val2_str=strchr(opt_val_str, ':');
|
|
if(opt_val2_str) {
|
|
*opt_val2_str++='\0';
|
|
ptr->opt_val[socket_type]->linger_val.l_linger=
|
|
(u_short)strtol(opt_val2_str, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return 1; /* FAILED */
|
|
} else {
|
|
ptr->opt_val[socket_type]->linger_val.l_linger=0;
|
|
}
|
|
ptr->opt_val[socket_type]->linger_val.l_onoff=
|
|
(u_short)strtol(opt_val_str, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return 1; /* FAILED */
|
|
return 0; /* OK */
|
|
case TYPE_TIMEVAL:
|
|
opt_val2_str=strchr(opt_val_str, ':');
|
|
if(opt_val2_str) {
|
|
*opt_val2_str++='\0';
|
|
ptr->opt_val[socket_type]->timeval_val.tv_usec=
|
|
(int)strtol(opt_val2_str, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return 1; /* FAILED */
|
|
} else {
|
|
ptr->opt_val[socket_type]->timeval_val.tv_usec=0;
|
|
}
|
|
ptr->opt_val[socket_type]->timeval_val.tv_sec=
|
|
(int)strtol(opt_val_str, &tmp_str, 10);
|
|
if(tmp_str==arg || *tmp_str) /* not a number */
|
|
return 1; /* FAILED */
|
|
return 0; /* OK */
|
|
case TYPE_STRING:
|
|
if(strlen(opt_val_str)+1>sizeof(OPT_UNION))
|
|
return 1; /* FAILED */
|
|
strcpy(ptr->opt_val[socket_type]->c_val, opt_val_str);
|
|
return 0; /* OK */
|
|
default:
|
|
; /* ANSI C compiler needs it */
|
|
}
|
|
return 1; /* FAILED */
|
|
}
|
|
|
|
/**************************************** OCSP */
|
|
|
|
#ifndef OPENSSL_NO_OCSP
|
|
|
|
NOEXPORT unsigned long parse_ocsp_flag(char *arg) {
|
|
struct {
|
|
char *name;
|
|
unsigned long value;
|
|
} ocsp_opts[] = {
|
|
{"NOCERTS", OCSP_NOCERTS},
|
|
{"NOINTERN", OCSP_NOINTERN},
|
|
{"NOSIGS", OCSP_NOSIGS},
|
|
{"NOCHAIN", OCSP_NOCHAIN},
|
|
{"NOVERIFY", OCSP_NOVERIFY},
|
|
{"NOEXPLICIT", OCSP_NOEXPLICIT},
|
|
{"NOCASIGN", OCSP_NOCASIGN},
|
|
{"NODELEGATED", OCSP_NODELEGATED},
|
|
{"NOCHECKS", OCSP_NOCHECKS},
|
|
{"TRUSTOTHER", OCSP_TRUSTOTHER},
|
|
{"RESPID_KEY", OCSP_RESPID_KEY},
|
|
{"NOTIME", OCSP_NOTIME},
|
|
{NULL, 0}
|
|
}, *option;
|
|
|
|
for(option=ocsp_opts; option->name; ++option)
|
|
if(!strcasecmp(option->name, arg))
|
|
return option->value;
|
|
return 0; /* FAILED */
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_OCSP) */
|
|
|
|
/**************************************** engine */
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
#define MAX_ENGINES 256
|
|
static ENGINE *engines[MAX_ENGINES]; /* table of engines for config parser */
|
|
static int current_engine;
|
|
static int engine_initialized;
|
|
|
|
NOEXPORT void engine_reset_list(void) {
|
|
current_engine=-1;
|
|
engine_initialized=1;
|
|
}
|
|
|
|
NOEXPORT char *engine_auto(void) {
|
|
ENGINE *e;
|
|
|
|
s_log(LOG_INFO, "Enabling automatic engine support");
|
|
ENGINE_register_all_complete();
|
|
/* rebuild the internal list of engines */
|
|
engine_reset_list();
|
|
for(e=ENGINE_get_first(); e; e=ENGINE_get_next(e)) {
|
|
if(++current_engine>=MAX_ENGINES)
|
|
return "Too many open engines";
|
|
engines[current_engine]=e;
|
|
s_log(LOG_INFO, "Engine #%d (%s) registered",
|
|
current_engine+1, ENGINE_get_id(e));
|
|
}
|
|
s_log(LOG_INFO, "Automatic engine support enabled");
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
NOEXPORT char *engine_open(const char *name) {
|
|
engine_init(); /* initialize the previous engine (if any) */
|
|
if(++current_engine>=MAX_ENGINES)
|
|
return "Too many open engines";
|
|
s_log(LOG_DEBUG, "Enabling support for engine \"%s\"", name);
|
|
engines[current_engine]=ENGINE_by_id(name);
|
|
if(!engines[current_engine]) {
|
|
sslerror("ENGINE_by_id");
|
|
return "Failed to open the engine";
|
|
}
|
|
engine_initialized=0;
|
|
if(ENGINE_ctrl(engines[current_engine], ENGINE_CTRL_SET_USER_INTERFACE,
|
|
0, UI_stunnel(), NULL)) {
|
|
s_log(LOG_NOTICE, "UI set for engine #%d (%s)",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]));
|
|
} else {
|
|
ERR_clear_error();
|
|
s_log(LOG_INFO, "UI not supported by engine #%d (%s)",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]));
|
|
}
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
NOEXPORT char *engine_ctrl(const char *cmd, const char *arg) {
|
|
if(current_engine<0)
|
|
return "No engine was defined";
|
|
if(!strcasecmp(cmd, "INIT")) /* special control command */
|
|
return engine_init();
|
|
if(arg)
|
|
s_log(LOG_DEBUG, "Executing engine control command %s:%s", cmd, arg);
|
|
else
|
|
s_log(LOG_DEBUG, "Executing engine control command %s", cmd);
|
|
if(!ENGINE_ctrl_cmd_string(engines[current_engine], cmd, arg, 0)) {
|
|
sslerror("ENGINE_ctrl_cmd_string");
|
|
return "Failed to execute the engine control command";
|
|
}
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
NOEXPORT char *engine_default(const char *list) {
|
|
if(current_engine<0)
|
|
return "No engine was defined";
|
|
if(!ENGINE_set_default_string(engines[current_engine], list)) {
|
|
sslerror("ENGINE_set_default_string");
|
|
return "Failed to set engine as default";
|
|
}
|
|
s_log(LOG_INFO, "Engine #%d (%s) set as default for %s",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]), list);
|
|
return NULL;
|
|
}
|
|
|
|
NOEXPORT char *engine_init(void) {
|
|
if(engine_initialized) /* either first or already initialized */
|
|
return NULL; /* OK */
|
|
s_log(LOG_DEBUG, "Initializing engine #%d (%s)",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]));
|
|
if(!ENGINE_init(engines[current_engine])) {
|
|
if(ERR_peek_last_error()) /* really an error */
|
|
sslerror("ENGINE_init");
|
|
else
|
|
s_log(LOG_ERR, "Engine #%d (%s) not initialized",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]));
|
|
return "Engine initialization failed";
|
|
}
|
|
#if 0
|
|
/* it is a bad idea to set the engine as default for all sections */
|
|
/* the "engine=auto" or "engineDefault" options should be used instead */
|
|
if(!ENGINE_set_default(engines[current_engine], ENGINE_METHOD_ALL)) {
|
|
sslerror("ENGINE_set_default");
|
|
return "Selecting default engine failed";
|
|
}
|
|
#endif
|
|
/* engines can add new algorithms */
|
|
#if OPENSSL_VERSION_NUMBER>=0x10100000L
|
|
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|
|
|
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
|
|
#else
|
|
OpenSSL_add_all_algorithms();
|
|
#endif
|
|
|
|
s_log(LOG_INFO, "Engine #%d (%s) initialized",
|
|
current_engine+1, ENGINE_get_id(engines[current_engine]));
|
|
engine_initialized=1;
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
NOEXPORT ENGINE *engine_get_by_id(const char *id) {
|
|
int i;
|
|
|
|
for(i=0; i<=current_engine; ++i)
|
|
if(!strcmp(id, ENGINE_get_id(engines[i])))
|
|
return engines[i];
|
|
return NULL;
|
|
}
|
|
|
|
NOEXPORT ENGINE *engine_get_by_num(const int i) {
|
|
if(i<0 || i>current_engine)
|
|
return NULL;
|
|
return engines[i];
|
|
}
|
|
|
|
#endif /* !defined(OPENSSL_NO_ENGINE) */
|
|
|
|
/**************************************** fatal error */
|
|
|
|
NOEXPORT void print_syntax(void) {
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Syntax:");
|
|
s_log(LOG_NOTICE, "stunnel "
|
|
#ifdef USE_WIN32
|
|
#ifndef _WIN32_WCE
|
|
"[ [-install | -uninstall | -start | -stop | -reload | -reopen | -exit] "
|
|
#endif
|
|
"[-quiet] "
|
|
#endif
|
|
"[<filename>] ] "
|
|
#ifndef USE_WIN32
|
|
"-fd <n> "
|
|
#endif
|
|
"| -help | -version | -sockets | -options");
|
|
s_log(LOG_NOTICE, " <filename> - use specified config file");
|
|
#ifdef USE_WIN32
|
|
#ifndef _WIN32_WCE
|
|
s_log(LOG_NOTICE, " -install - install NT service");
|
|
s_log(LOG_NOTICE, " -uninstall - uninstall NT service");
|
|
s_log(LOG_NOTICE, " -start - start NT service");
|
|
s_log(LOG_NOTICE, " -stop - stop NT service");
|
|
s_log(LOG_NOTICE, " -reload - reload configuration for NT service");
|
|
s_log(LOG_NOTICE, " -reopen - reopen log file for NT service");
|
|
s_log(LOG_NOTICE, " -exit - exit an already started stunnel");
|
|
#endif
|
|
s_log(LOG_NOTICE, " -quiet - don't display message boxes");
|
|
#else
|
|
s_log(LOG_NOTICE, " -fd <n> - read the config file from a file descriptor");
|
|
#endif
|
|
s_log(LOG_NOTICE, " -help - get config file help");
|
|
s_log(LOG_NOTICE, " -version - display version and defaults");
|
|
s_log(LOG_NOTICE, " -sockets - display default socket options");
|
|
s_log(LOG_NOTICE, " -options - display supported TLS options");
|
|
}
|
|
|
|
/**************************************** various supporting functions */
|
|
|
|
NOEXPORT void name_list_append(NAME_LIST **ptr, char *name) {
|
|
while(*ptr) /* find the null pointer */
|
|
ptr=&(*ptr)->next;
|
|
*ptr=str_alloc(sizeof(NAME_LIST));
|
|
(*ptr)->name=str_dup(name);
|
|
(*ptr)->next=NULL;
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
|
|
NOEXPORT char **argalloc(char *str) { /* allocate 'exec' argumets */
|
|
size_t max_arg, i;
|
|
char *ptr, **retval;
|
|
|
|
max_arg=strlen(str)/2+1;
|
|
ptr=str_dup(str);
|
|
retval=str_alloc((max_arg+1)*sizeof(char *));
|
|
i=0;
|
|
while(*ptr && i<max_arg) {
|
|
retval[i++]=ptr;
|
|
while(*ptr && !isspace((unsigned char)*ptr))
|
|
++ptr;
|
|
while(*ptr && isspace((unsigned char)*ptr))
|
|
*ptr++='\0';
|
|
}
|
|
retval[i]=NULL; /* to show that it's null-terminated */
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
/* end of options.c */
|