2408 lines
73 KiB
C
2408 lines
73 KiB
C
/*
|
|
* stunnel Universal SSL tunnel
|
|
* Copyright (C) 1998-2012 Michal Trojnara <Michal.Trojnara@mirt.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, see <http://www.gnu.org/licenses>.
|
|
*
|
|
* Linking stunnel statically or dynamically with other modules is making
|
|
* a combined work based on stunnel. Thus, the terms and conditions of
|
|
* the GNU General Public License cover the whole combination.
|
|
*
|
|
* In addition, as a special exception, the copyright holder of stunnel
|
|
* gives you permission to combine stunnel with free software programs or
|
|
* libraries that are released under the GNU LGPL and with code included
|
|
* in the standard release of OpenSSL under the OpenSSL License (or
|
|
* modified versions of such code, with unchanged license). You may copy
|
|
* and distribute such a system following the terms of the GNU GPL for
|
|
* stunnel and the licenses of the other code concerned.
|
|
*
|
|
* Note that people who make modified versions of stunnel are not obligated
|
|
* to grant this special exception for their modified versions; it is their
|
|
* choice whether to do so. The GNU General Public License gives permission
|
|
* to release a modified version without this exception; this exception
|
|
* also makes it possible to release a modified version which carries
|
|
* forward this exception.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "prototypes.h"
|
|
|
|
#if !defined(OPENSSL_NO_TLS1)
|
|
#define DEFAULT_SSLVER_CLIENT "TLSv1"
|
|
#elif !defined(OPENSSL_NO_SSL3)
|
|
#define DEFAULT_SSLVER_CLIENT "SSLv3"
|
|
#elif !defined(OPENSSL_NO_SSL2)
|
|
#define DEFAULT_SSLVER_CLIENT "SSLv2"
|
|
#else /* OPENSSL_NO_TLS1, OPENSSL_NO_SSL3, OPENSSL_NO_SSL2 */
|
|
#error No supported SSL methods found
|
|
#endif /* OPENSSL_NO_TLS1, OPENSSL_NO_SSL3, OPENSSL_NO_SSL2 */
|
|
#define DEFAULT_SSLVER_SERVER "all"
|
|
|
|
#if defined(_WIN32_WCE) && !defined(CONFDIR)
|
|
#define CONFDIR "\\stunnel"
|
|
#endif
|
|
|
|
#ifdef USE_WIN32
|
|
#define CONFSEPARATOR "\\"
|
|
#else
|
|
#define CONFSEPARATOR "/"
|
|
#endif
|
|
|
|
#define CONFLINELEN (16*1024)
|
|
|
|
static void init_globals(void);
|
|
static int init_section(SERVICE_OPTIONS *);
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
static int init_sni(SERVICE_OPTIONS *);
|
|
#endif
|
|
|
|
static int parse_debug_level(char *);
|
|
|
|
static int parse_ssl_option(char *);
|
|
|
|
static int print_socket_options(void);
|
|
static char *print_option(int, OPT_UNION *);
|
|
static int parse_socket_option(char *);
|
|
|
|
#ifdef HAVE_OSSL_OCSP_H
|
|
static char *parse_ocsp_url(SERVICE_OPTIONS *, char *);
|
|
static unsigned long parse_ocsp_flag(char *);
|
|
#endif /* HAVE_OSSL_OCSP_H */
|
|
|
|
#ifdef HAVE_OSSL_ENGINE_H
|
|
static char *open_engine(const char *);
|
|
static char *ctrl_engine(const char *, const char *);
|
|
static char *init_engine(void);
|
|
static void close_engine(void);
|
|
static ENGINE *get_engine(int);
|
|
#endif
|
|
|
|
static void print_syntax(void);
|
|
static void config_error(int, const char *, const char *);
|
|
static void section_error(const char *, const char *);
|
|
#ifndef USE_WIN32
|
|
static char **argalloc(char *);
|
|
#endif
|
|
|
|
GLOBAL_OPTIONS global_options;
|
|
SERVICE_OPTIONS service_options;
|
|
|
|
static GLOBAL_OPTIONS new_global_options;
|
|
static SERVICE_OPTIONS new_service_options;
|
|
|
|
typedef enum {
|
|
CMD_INIT, /* initialize */
|
|
CMD_EXEC,
|
|
CMD_DEFAULT,
|
|
CMD_HELP
|
|
} CMD;
|
|
|
|
static char *option_not_found=
|
|
"Specified option name is not valid here";
|
|
|
|
static char *stunnel_cipher_list=
|
|
"ALL:!SSLv2:!aNULL:!EXP:!LOW:-MEDIUM:RC4:+HIGH";
|
|
|
|
/**************************************** global options */
|
|
|
|
static char *parse_global_option(CMD cmd, char *opt, char *arg) {
|
|
char *tmpstr;
|
|
#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, "Global options:");
|
|
}
|
|
|
|
/* chroot */
|
|
#ifdef HAVE_CHROOT
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = directory to chroot stunnel process", "chroot");
|
|
break;
|
|
}
|
|
#endif /* HAVE_CHROOT */
|
|
|
|
/* compression */
|
|
#ifndef OPENSSL_NO_COMP
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.compression=COMP_NONE;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "compression"))
|
|
break;
|
|
if(SSLeay()>=0x00908051L && !strcasecmp(arg, "deflate"))
|
|
new_global_options.compression=COMP_DEFLATE;
|
|
else if(!strcasecmp(arg, "zlib"))
|
|
new_global_options.compression=COMP_ZLIB;
|
|
else if(!strcasecmp(arg, "rle"))
|
|
new_global_options.compression=COMP_RLE;
|
|
else
|
|
return "Specified compression type is not available";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = compression type",
|
|
"compression");
|
|
break;
|
|
}
|
|
#endif /* OPENSSL_NO_COMP */
|
|
|
|
/* debug */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.debug_level=LOG_NOTICE;
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
new_global_options.facility=LOG_DAEMON;
|
|
#endif
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "debug"))
|
|
break;
|
|
if(parse_debug_level(arg))
|
|
return "Illegal debug argument";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
#if !defined (USE_WIN32) && !defined (__vms)
|
|
s_log(LOG_NOTICE, "%-15s = %s", "debug", "daemon.notice");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-15s = %s", "debug", "notice");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = [facility].level (e.g. daemon.info)", "debug");
|
|
break;
|
|
}
|
|
|
|
/* EGD */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
#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_DEFAULT:
|
|
#ifdef EGD_SOCKET
|
|
s_log(LOG_NOTICE, "%-15s = %s", "EGD", EGD_SOCKET);
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = path to Entropy Gathering Daemon socket", "EGD");
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_OSSL_ENGINE_H
|
|
/* engine */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engine"))
|
|
break;
|
|
return open_engine(arg);
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = auto|engine_id",
|
|
"engine");
|
|
break;
|
|
}
|
|
|
|
/* engineCtrl */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineCtrl"))
|
|
break;
|
|
tmpstr=strchr(arg, ':');
|
|
if(tmpstr)
|
|
*tmpstr++='\0';
|
|
return ctrl_engine(arg, tmpstr);
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = cmd[:arg]",
|
|
"engineCtrl");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* fips */
|
|
#ifdef USE_FIPS
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.option.fips=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "fips"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.fips=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.fips=0;
|
|
else
|
|
return "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no FIPS 140-2 mode",
|
|
"fips");
|
|
break;
|
|
}
|
|
#endif /* USE_FIPS */
|
|
|
|
/* foreground */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.option.foreground=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "foreground"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.foreground=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.foreground=0;
|
|
else
|
|
return "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no foreground mode (don't fork, log to stderr)",
|
|
"foreground");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* output */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = file to append log messages", "output");
|
|
break;
|
|
}
|
|
|
|
/* pid */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.pidfile=PIDFILE;
|
|
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_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %s", "pid", PIDFILE);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = pid file (empty to disable creating)", "pid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* RNDbytes */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.random_bytes=RANDOM_BYTES;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "RNDbytes"))
|
|
break;
|
|
new_global_options.random_bytes=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal number of bytes to read from random seed files";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d", "RNDbytes", RANDOM_BYTES);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = bytes to read from random seed files", "RNDbytes");
|
|
break;
|
|
}
|
|
|
|
/* RNDfile */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
#ifdef RANDOM_FILE
|
|
s_log(LOG_NOTICE, "%-15s = %s", "RNDfile", RANDOM_FILE);
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = path to file with random seed data", "RNDfile");
|
|
break;
|
|
}
|
|
|
|
/* RNDoverwrite */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = yes", "RNDoverwrite");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no overwrite seed datafiles with new random data",
|
|
"RNDoverwrite");
|
|
break;
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
/* service */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = service name", "service");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifndef USE_WIN32
|
|
/* setgid */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.gid=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "setgid"))
|
|
break;
|
|
gr=getgrnam(arg);
|
|
if(gr) {
|
|
new_global_options.gid=gr->gr_gid;
|
|
return NULL; /* OK */
|
|
}
|
|
new_global_options.gid=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal GID";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = groupname for setgid()", "setgid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifndef USE_WIN32
|
|
/* setuid */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.uid=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "setuid"))
|
|
break;
|
|
pw=getpwnam(arg);
|
|
if(pw) {
|
|
new_global_options.uid=pw->pw_uid;
|
|
return NULL; /* OK */
|
|
}
|
|
new_global_options.uid=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal UID";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = username for setuid()", "setuid");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* socket */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "socket"))
|
|
break;
|
|
if(parse_socket_option(arg))
|
|
return "Illegal socket option";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = a|l|r:option=value[:value]", "socket");
|
|
s_log(LOG_NOTICE, "%18sset an option on accept/local/remote socket", "");
|
|
break;
|
|
}
|
|
|
|
/* syslog */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
new_global_options.option.syslog=1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "syslog"))
|
|
break;
|
|
if(!strcasecmp(arg, "yes"))
|
|
new_global_options.option.syslog=1;
|
|
else if(!strcasecmp(arg, "no"))
|
|
new_global_options.option.syslog=0;
|
|
else
|
|
return "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no send logging messages to syslog",
|
|
"syslog");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* taskbar */
|
|
#ifdef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = yes", "taskbar");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no enable the taskbar icon", "taskbar");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if(cmd==CMD_EXEC)
|
|
return option_not_found;
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
/**************************************** service-level options */
|
|
|
|
static char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section,
|
|
char *opt, char *arg) {
|
|
char *tmpstr;
|
|
int tmpnum;
|
|
|
|
if(cmd==CMD_DEFAULT || cmd==CMD_HELP) {
|
|
s_log(LOG_NOTICE, " ");
|
|
s_log(LOG_NOTICE, "Service-level options:");
|
|
}
|
|
|
|
/* accept */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.accept=0;
|
|
memset(§ion->local_addr, 0, sizeof(SOCKADDR_UNION));
|
|
section->local_addr.in.sin_family=AF_INET;
|
|
section->fd=-1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "accept"))
|
|
break;
|
|
section->option.accept=1;
|
|
if(!name2addr(§ion->local_addr, arg, DEFAULT_ANY))
|
|
return "Failed to resolve accepting address";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = [host:]port accept connections on specified host:port",
|
|
"accept");
|
|
break;
|
|
}
|
|
|
|
/* CApath */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
#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_DEFAULT:
|
|
#if 0
|
|
s_log(LOG_NOTICE, "%-15s = %s", "CApath",
|
|
section->ca_dir ? section->ca_dir : "(none)");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = CA certificate directory for 'verify' option",
|
|
"CApath");
|
|
break;
|
|
}
|
|
|
|
/* CAfile */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
#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_DEFAULT:
|
|
#if 0
|
|
s_log(LOG_NOTICE, "%-15s = %s", "CAfile",
|
|
section->ca_file ? section->ca_file : "(none)");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = CA certificate file for 'verify' option",
|
|
"CAfile");
|
|
break;
|
|
}
|
|
|
|
/* cert */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->cert=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "cert"))
|
|
break;
|
|
section->cert=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break; /* no default certificate */
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = certificate chain", "cert");
|
|
break;
|
|
}
|
|
|
|
/* ciphers */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->cipher_list=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ciphers"))
|
|
break;
|
|
section->cipher_list=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
#ifdef USE_FIPS
|
|
s_log(LOG_NOTICE, "%-15s = %s %s", "ciphers",
|
|
"FIPS", "(with \"fips = yes\")");
|
|
s_log(LOG_NOTICE, "%-15s = %s %s", "ciphers",
|
|
stunnel_cipher_list, "(with \"fips = no\")");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-15s = %s", "ciphers", stunnel_cipher_list);
|
|
#endif /* USE_FIPS */
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = list of permitted SSL ciphers", "ciphers");
|
|
break;
|
|
}
|
|
|
|
/* client */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no client mode (remote service uses SSL)",
|
|
"client");
|
|
break;
|
|
}
|
|
|
|
/* connect */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.remote=0;
|
|
section->connect_name=NULL;
|
|
section->connect_addr.num=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "connect"))
|
|
break;
|
|
section->option.remote=1;
|
|
section->connect_name=str_dup(arg);
|
|
if(!section->option.delayed_lookup &&
|
|
!name2addrlist(§ion->connect_addr, arg, DEFAULT_LOOPBACK)) {
|
|
s_log(LOG_INFO, "Cannot resolve '%s' - delaying DNS lookup", arg);
|
|
section->option.delayed_lookup=1;
|
|
}
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = [host:]port connect remote host:port",
|
|
"connect");
|
|
break;
|
|
}
|
|
|
|
/* CRLpath */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = CRL directory", "CRLpath");
|
|
break;
|
|
}
|
|
|
|
/* CRLfile */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = CRL file", "CRLfile");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
/* curve */
|
|
#define DEFAULT_CURVE NID_X9_62_prime256v1
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %s", "curve", OBJ_nid2ln(DEFAULT_CURVE));
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = ECDH curve name", "curve");
|
|
break;
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
/* delay */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no delay DNS lookup for 'connect' option",
|
|
"delay");
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_OSSL_ENGINE_H
|
|
/* engineNum */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "engineNum"))
|
|
break;
|
|
tmpnum=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal engine number";
|
|
section->engine=get_engine(tmpnum);
|
|
if(!section->engine)
|
|
return "Illegal engine number";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = number of engine to read the key from",
|
|
"engineNum");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* exec */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.program=0;
|
|
section->execname=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "exec"))
|
|
break;
|
|
section->option.program=1;
|
|
section->execname=str_dup(arg);
|
|
#ifdef USE_WIN32
|
|
section->execargs=str_dup(arg);
|
|
#else
|
|
if(!section->execargs) {
|
|
section->execargs=str_alloc(2*sizeof(char *));
|
|
section->execargs[0]=section->execname;
|
|
section->execargs[1]=NULL; /* to show that it's null-terminated */
|
|
}
|
|
#endif
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = file execute local inetd-type program",
|
|
"exec");
|
|
break;
|
|
}
|
|
|
|
/* execargs */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->execargs=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "execargs"))
|
|
break;
|
|
#ifdef USE_WIN32
|
|
section->execargs=str_dup(arg);
|
|
#else
|
|
section->execargs=argalloc(arg);
|
|
#endif
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = arguments for 'exec' (including $0)",
|
|
"execargs");
|
|
break;
|
|
}
|
|
|
|
/* failover */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->failover=FAILOVER_RR;
|
|
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 "Argument should be either 'rr' or 'prio'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = rr|prio failover strategy",
|
|
"failover");
|
|
break;
|
|
}
|
|
|
|
/* ident */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->username=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ident"))
|
|
break;
|
|
section->username=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = username for IDENT (RFC 1413) checking", "ident");
|
|
break;
|
|
}
|
|
|
|
/* key */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->key=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "key"))
|
|
break;
|
|
section->key=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = certificate private key", "key");
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_LIBWRAP
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.libwrap=1; /* enable 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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no use /etc/hosts.allow and /etc/hosts.deny",
|
|
"libwrap");
|
|
break;
|
|
}
|
|
#endif /* USE_LIBWRAP */
|
|
|
|
/* local */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.local=0;
|
|
memset(§ion->source_addr, 0, sizeof(SOCKADDR_UNION));
|
|
section->source_addr.in.sin_family=AF_INET;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "local"))
|
|
break;
|
|
section->option.local=1;
|
|
if(!hostport2addr(§ion->source_addr, arg, "0"))
|
|
return "Failed to resolve local address";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = IP address to be used as source for remote"
|
|
" connections", "local");
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_OSSL_OCSP_H
|
|
|
|
/* OCSP */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->option.ocsp=0;
|
|
memset(§ion->ocsp_addr, 0, sizeof(SOCKADDR_UNION));
|
|
section->ocsp_addr.in.sin_family=AF_INET;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "ocsp"))
|
|
break;
|
|
section->option.ocsp=1;
|
|
return parse_ocsp_url(section, arg);
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = OCSP server URL", "ocsp");
|
|
break;
|
|
}
|
|
|
|
/* OCSPflag */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->ocsp_flags=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "OCSPflag"))
|
|
break;
|
|
tmpnum=parse_ocsp_flag(arg);
|
|
if(!tmpnum)
|
|
return "Illegal OCSP flag";
|
|
section->ocsp_flags|=tmpnum;
|
|
return NULL;
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = OCSP server flags", "OCSPflag");
|
|
break;
|
|
}
|
|
|
|
#endif /* HAVE_OSSL_OCSP_H */
|
|
|
|
/* options */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->ssl_options=0;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "options"))
|
|
break;
|
|
tmpnum=parse_ssl_option(arg);
|
|
if(!tmpnum)
|
|
return "Illegal SSL option";
|
|
section->ssl_options|=tmpnum;
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = SSL option", "options");
|
|
s_log(LOG_NOTICE, "%18sset an SSL option", "");
|
|
break;
|
|
}
|
|
|
|
/* protocol */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->protocol=-1;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocol"))
|
|
break;
|
|
section->protocol=find_protocol_id(arg);
|
|
if(section->protocol<0)
|
|
return "Unknown protocol";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = protocol to negotiate before SSL initialization",
|
|
"protocol");
|
|
s_log(LOG_NOTICE, "%18scurrently supported: cifs, connect, imap, nntp, pgsql, pop3, proxy, smtp", "");
|
|
break;
|
|
}
|
|
|
|
/* protocolAuthentication */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->protocol_authentication="basic";
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolAuthentication"))
|
|
break;
|
|
section->protocol_authentication=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = authentication type for protocol negotiations",
|
|
"protocolAuthentication");
|
|
break;
|
|
}
|
|
|
|
/* protocolHost */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->protocol_host=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolHost"))
|
|
break;
|
|
section->protocol_host=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = host:port for protocol negotiations",
|
|
"protocolHost");
|
|
break;
|
|
}
|
|
|
|
/* protocolPassword */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->protocol_password=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolPassword"))
|
|
break;
|
|
section->protocol_password=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = password for protocol negotiations",
|
|
"protocolPassword");
|
|
break;
|
|
}
|
|
|
|
/* protocolUsername */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->protocol_username=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "protocolUsername"))
|
|
break;
|
|
section->protocol_username=str_dup(arg);
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = username for protocol negotiations",
|
|
"protocolUsername");
|
|
break;
|
|
}
|
|
|
|
/* pty */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no allocate pseudo terminal for 'exec' option",
|
|
"pty");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* retry */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 "Argument should be either 'yes' or 'no'";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = yes|no retry connect+exec section",
|
|
"retry");
|
|
break;
|
|
}
|
|
|
|
/* session */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->session_timeout=300L;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "session"))
|
|
break;
|
|
section->session_timeout=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal session timeout";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %ld seconds", "session", 300L);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = session cache timeout (in seconds)", "session");
|
|
break;
|
|
}
|
|
|
|
/* sessiond */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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 beeing executed */
|
|
section->ssl_options|=SSL_OP_NO_TICKET;
|
|
#endif
|
|
if(!name2addr(§ion->sessiond_addr, arg, DEFAULT_LOOPBACK))
|
|
return "Failed to resolve sessiond server address";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = [host:]port use sessiond at host:port",
|
|
"sessiond");
|
|
break;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
/* sni */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = master_service:host_name for an SNI virtual service",
|
|
"sni");
|
|
break;
|
|
}
|
|
#endif /* OPENSSL_NO_TLSEXT */
|
|
|
|
/* sslVersion */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->client_method=NULL;
|
|
section->server_method=NULL;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "sslVersion"))
|
|
break;
|
|
if(!strcasecmp(arg, "all")) {
|
|
section->client_method=(SSL_METHOD *)SSLv23_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv23_server_method();
|
|
} else if(!strcasecmp(arg, "SSLv2")) {
|
|
#if !defined(OPENSSL_NO_SSL2)
|
|
section->client_method=(SSL_METHOD *)SSLv2_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv2_server_method();
|
|
#else
|
|
return "SSLv2 not supported";
|
|
#endif
|
|
} else if(!strcasecmp(arg, "SSLv3")) {
|
|
#if !defined(OPENSSL_NO_SSL3)
|
|
section->client_method=(SSL_METHOD *)SSLv3_client_method();
|
|
section->server_method=(SSL_METHOD *)SSLv3_server_method();
|
|
#else
|
|
return "SSLv3 not supported";
|
|
#endif
|
|
} else if(!strcasecmp(arg, "TLSv1")) {
|
|
#if !defined(OPENSSL_NO_TLS1)
|
|
section->client_method=(SSL_METHOD *)TLSv1_client_method();
|
|
section->server_method=(SSL_METHOD *)TLSv1_server_method();
|
|
#else
|
|
return "TLSv1 not supported";
|
|
#endif
|
|
} else
|
|
return "Incorrect version of SSL protocol";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
#ifdef USE_FIPS
|
|
s_log(LOG_NOTICE, "%-15s = TLSv1 (with \"fips = yes\")",
|
|
"sslVersion");
|
|
s_log(LOG_NOTICE, "%-15s = " DEFAULT_SSLVER_CLIENT " for client, "
|
|
DEFAULT_SSLVER_SERVER " for server (with \"fips = no\")",
|
|
"sslVersion");
|
|
#else
|
|
s_log(LOG_NOTICE, "%-15s = " DEFAULT_SSLVER_CLIENT " for client, "
|
|
DEFAULT_SSLVER_SERVER " for server", "sslVersion");
|
|
#endif
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = all|SSLv2|SSLv3|TLSv1 SSL method", "sslVersion");
|
|
break;
|
|
}
|
|
|
|
#ifndef USE_FORK
|
|
/* stack */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->stack_size=DEFAULT_STACK_SIZE;
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "stack"))
|
|
break;
|
|
section->stack_size=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal thread stack size";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d bytes", "stack", DEFAULT_STACK_SIZE);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = thread stack size (in bytes)", "stack");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* TIMEOUTbusy */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->timeout_busy=300; /* 5 minutes */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTbusy"))
|
|
break;
|
|
section->timeout_busy=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal busy timeout";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d seconds", "TIMEOUTbusy", 300);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = seconds to wait for expected data", "TIMEOUTbusy");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTclose */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->timeout_close=60; /* 1 minute */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTclose"))
|
|
break;
|
|
section->timeout_close=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal close timeout";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d seconds", "TIMEOUTclose", 60);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = seconds to wait for close_notify"
|
|
" (set to 0 for buggy MSIE)", "TIMEOUTclose");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTconnect */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->timeout_connect=10; /* 10 seconds */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTconnect"))
|
|
break;
|
|
section->timeout_connect=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal connect timeout";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d seconds", "TIMEOUTconnect", 10);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = seconds to connect remote host", "TIMEOUTconnect");
|
|
break;
|
|
}
|
|
|
|
/* TIMEOUTidle */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->timeout_idle=43200; /* 12 hours */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "TIMEOUTidle"))
|
|
break;
|
|
section->timeout_idle=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Illegal idle timeout";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = %d seconds", "TIMEOUTidle", 43200);
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE, "%-15s = seconds to keep an idle connection", "TIMEOUTidle");
|
|
break;
|
|
}
|
|
|
|
/* transparent */
|
|
#ifndef USE_WIN32
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
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;
|
|
#ifdef SO_ORIGINAL_DST
|
|
} 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;
|
|
#endif
|
|
} else
|
|
return "Selected transparent proxy mode is not available";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-15s = none|source|destination|both transparent proxy mode",
|
|
"transparent");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* verify */
|
|
switch(cmd) {
|
|
case CMD_INIT:
|
|
section->verify_level=-1; /* do not even request a certificate */
|
|
break;
|
|
case CMD_EXEC:
|
|
if(strcasecmp(opt, "verify"))
|
|
break;
|
|
section->verify_level=strtol(arg, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* not a number */
|
|
return "Bad verify level";
|
|
if(section->verify_level<0 || section->verify_level>4)
|
|
return "Bad verify level";
|
|
return NULL; /* OK */
|
|
case CMD_DEFAULT:
|
|
s_log(LOG_NOTICE, "%-15s = none", "verify");
|
|
break;
|
|
case CMD_HELP:
|
|
s_log(LOG_NOTICE,
|
|
"%-15s = level of peer certificate verification", "verify");
|
|
s_log(LOG_NOTICE,
|
|
"%18slevel 0 - request and ignore peer certificate", "");
|
|
s_log(LOG_NOTICE,
|
|
"%18slevel 1 - only validate peer certificate if present", "");
|
|
s_log(LOG_NOTICE,
|
|
"%18slevel 2 - always require a valid peer certificate", "");
|
|
s_log(LOG_NOTICE,
|
|
"%18slevel 3 - verify peer with locally installed certificate", "");
|
|
s_log(LOG_NOTICE,
|
|
"%18slevel 4 - ignore CA chain and only verify peer certificate", "");
|
|
break;
|
|
}
|
|
|
|
if(cmd==CMD_EXEC)
|
|
return option_not_found;
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
/**************************************** parse commandline parameters */
|
|
|
|
int parse_commandline(char *name, char *parameter) {
|
|
if(!name)
|
|
#ifdef CONFDIR
|
|
name=CONFDIR CONFSEPARATOR "stunnel.conf";
|
|
#else
|
|
name="stunnel.conf";
|
|
#endif
|
|
|
|
if(!strcasecmp(name, "-help")) {
|
|
parse_global_option(CMD_HELP, NULL, NULL);
|
|
parse_service_option(CMD_HELP, NULL, NULL, NULL);
|
|
log_flush(LOG_MODE_INFO);
|
|
return 1;
|
|
}
|
|
|
|
if(!strcasecmp(name, "-version")) {
|
|
parse_global_option(CMD_DEFAULT, NULL, NULL);
|
|
parse_service_option(CMD_DEFAULT, NULL, NULL, NULL);
|
|
log_flush(LOG_MODE_INFO);
|
|
return 1;
|
|
}
|
|
|
|
if(!strcasecmp(name, "-sockets")) {
|
|
print_socket_options();
|
|
log_flush(LOG_MODE_INFO);
|
|
return 1;
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
if(!strcasecmp(name, "-fd")) {
|
|
if(!parameter) {
|
|
s_log(LOG_ERR, "No file descriptor specified");
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
if(parse_conf(parameter, CONF_FD))
|
|
return 1;
|
|
} else
|
|
#else
|
|
(void)parameter; /* skip warning about unused parameter */
|
|
#endif
|
|
if(parse_conf(name, CONF_FILE))
|
|
return 1;
|
|
apply_conf();
|
|
return 0;
|
|
}
|
|
|
|
/**************************************** parse configuration file */
|
|
|
|
int parse_conf(char *name, CONF_TYPE type) {
|
|
DISK_FILE *df;
|
|
char line_text[CONFLINELEN], *errstr;
|
|
char config_line[CONFLINELEN], *config_opt, *config_arg;
|
|
int line_number, i;
|
|
SERVICE_OPTIONS *section, *new_section;
|
|
static char *filename=NULL; /* a copy of config file name for reloading */
|
|
#ifndef USE_WIN32
|
|
int fd;
|
|
char *tmpstr;
|
|
#endif
|
|
|
|
if(name) /* not reload */
|
|
filename=str_dup(name);
|
|
|
|
s_log(LOG_NOTICE, "Reading configuration from %s %s",
|
|
type==CONF_FD ? "descriptor" : "file", filename);
|
|
#ifndef USE_WIN32
|
|
if(type==CONF_FD) { /* file descriptor */
|
|
fd=strtol(filename, &tmpstr, 10);
|
|
if(tmpstr==filename || *tmpstr) { /* not a number */
|
|
s_log(LOG_ERR, "Invalid file descriptor number");
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
df=file_fdopen(fd);
|
|
} else
|
|
#endif
|
|
df=file_open(filename, 0);
|
|
if(!df) {
|
|
s_log(LOG_ERR, "Cannot read configuration");
|
|
if(type!=CONF_RELOAD)
|
|
print_syntax();
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
section=&new_service_options;
|
|
parse_global_option(CMD_INIT, NULL, NULL);
|
|
parse_service_option(CMD_INIT, section, NULL, NULL);
|
|
if(type!=CONF_RELOAD) { /* provide defaults for gui.c */
|
|
memcpy(&global_options, &new_global_options, sizeof(GLOBAL_OPTIONS));
|
|
memcpy(&service_options, &new_service_options, sizeof(SERVICE_OPTIONS));
|
|
}
|
|
|
|
line_number=0;
|
|
while(file_getline(df, line_text, CONFLINELEN)>=0) {
|
|
memcpy(config_line, line_text, CONFLINELEN);
|
|
++line_number;
|
|
config_opt=config_line;
|
|
while(isspace((unsigned char)*config_opt))
|
|
++config_opt; /* remove initial whitespaces */
|
|
for(i=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 */
|
|
if(!new_service_options.next) {
|
|
/* FIPS needs to be initialized as early as possible */
|
|
if(ssl_configure(&new_global_options)) { /* configure global SSL settings */
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
init_globals(); /* defaults need to be set before other options are parsed */
|
|
}
|
|
++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) {
|
|
config_error(line_number, line_text, "No '=' found");
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
*config_arg++='\0'; /* split into option name and argument value */
|
|
for(i=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 */
|
|
errstr=parse_service_option(CMD_EXEC, section, config_opt, config_arg);
|
|
if(!new_service_options.next && errstr==option_not_found)
|
|
errstr=parse_global_option(CMD_EXEC, config_opt, config_arg);
|
|
if(errstr) {
|
|
config_error(line_number, line_text, errstr);
|
|
file_close(df);
|
|
return 1;
|
|
}
|
|
}
|
|
file_close(df);
|
|
|
|
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 section [%s]", section->servname);
|
|
if(init_section(section))
|
|
return 1;
|
|
}
|
|
} else { /* inetd mode: need to initialize global options */
|
|
if(ssl_configure(&new_global_options)) /* configure global SSL settings */
|
|
return 1;
|
|
init_globals();
|
|
s_log(LOG_INFO, "Initializing inetd mode configuration");
|
|
if(init_section(&new_service_options))
|
|
return 1;
|
|
}
|
|
|
|
s_log(LOG_NOTICE, "Configuration successful");
|
|
return 0;
|
|
}
|
|
|
|
void apply_conf() { /* can be used once the configuration was validated */
|
|
/* 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));
|
|
#ifdef USE_WIN32
|
|
PostMessage(hwnd, WM_VALID_CONFIG, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
/**************************************** validate and initialize configuration */
|
|
|
|
static void init_globals() {
|
|
#ifdef HAVE_OSSL_ENGINE_H
|
|
close_engine();
|
|
#endif
|
|
|
|
/* prepare default SSL methods */
|
|
#ifdef USE_FIPS
|
|
if(new_global_options.option.fips) {
|
|
if(!new_service_options.cipher_list)
|
|
new_service_options.cipher_list="FIPS";
|
|
if(!new_service_options.client_method)
|
|
new_service_options.client_method=(SSL_METHOD *)TLSv1_client_method();
|
|
if(!new_service_options.server_method)
|
|
new_service_options.server_method=(SSL_METHOD *)TLSv1_server_method();
|
|
return;
|
|
}
|
|
#endif /* USE_FIPS */
|
|
if(!new_service_options.cipher_list)
|
|
new_service_options.cipher_list=stunnel_cipher_list;
|
|
if(!new_service_options.client_method)
|
|
#if !defined(OPENSSL_NO_TLS1)
|
|
new_service_options.client_method=(SSL_METHOD *)TLSv1_client_method();
|
|
#elif !defined(OPENSSL_NO_SSL3)
|
|
new_service_options.client_method=(SSL_METHOD *)SSLv3_client_method();
|
|
#elif !defined(OPENSSL_NO_SSL2)
|
|
new_service_options.client_method=(SSL_METHOD *)SSLv2_client_method();
|
|
#else /* OPENSSL_NO_TLS1, OPENSSL_NO_SSL3, OPENSSL_NO_SSL2 */
|
|
#error No supported SSL methods found
|
|
#endif /* OPENSSL_NO_TLS1, OPENSSL_NO_SSL3, OPENSSL_NO_SSL2 */
|
|
/* SSLv23_server_method() is an always available catch-all */
|
|
if(!new_service_options.server_method)
|
|
new_service_options.server_method=(SSL_METHOD *)SSLv23_server_method();
|
|
}
|
|
|
|
static int init_section(SERVICE_OPTIONS *section) {
|
|
#ifdef USE_FIPS
|
|
if(new_global_options.option.fips &&
|
|
((section->option.client &&
|
|
section->client_method!=(SSL_METHOD *)TLSv1_client_method()) ||
|
|
(!section->option.client &&
|
|
section->server_method!=(SSL_METHOD *)TLSv1_server_method()))) {
|
|
section_error(section->servname, "sslVersion = TLSv1 is required in FIPS mode");
|
|
return 1;
|
|
}
|
|
#endif /* USE_FIPS */
|
|
if(!section->option.client && !section->cert) {
|
|
section_error(section->servname, "SSL server needs a certificate");
|
|
return 1;
|
|
}
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
if(init_sni(section))
|
|
return 1;
|
|
#endif
|
|
if(context_init(section)) /* initialize SSL context */
|
|
return 1;
|
|
|
|
if(new_service_options.next) { /* daemon mode checks */
|
|
if((unsigned int)section->option.accept
|
|
+ (unsigned int)section->option.program
|
|
+ (unsigned int)section->option.remote
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
+ (unsigned int)section->option.sni
|
|
#endif /* OPENSSL_NO_TLSEXT */
|
|
#ifndef USE_WIN32
|
|
+ (unsigned int)section->option.transparent_dst
|
|
#endif /* USE_WIN32 */
|
|
!=2) {
|
|
section_error(section->servname, "Each service must define two endpoints");
|
|
return 1;
|
|
}
|
|
} else { /* inetd mode checks */
|
|
if(section->option.accept) {
|
|
s_log(LOG_ERR, "Accept option is not allowed in inetd mode");
|
|
s_log(LOG_ERR, "Remove accept option or define a [section]");
|
|
return 1;
|
|
}
|
|
if(!section->option.remote && !section->execname) {
|
|
s_log(LOG_ERR, "Inetd mode must have 'connect' or 'exec' options");
|
|
return 1;
|
|
}
|
|
#if 0
|
|
/* TODO: some additional checks could be useful */
|
|
if((unsigned int)section->option.program +
|
|
(unsigned int)section->option.remote != 1)
|
|
section_error(section->servname, "Single endpoint is required in inetd mode");
|
|
#endif
|
|
}
|
|
return 0; /* all tests passed -- continue program execution */
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
static int init_sni(SERVICE_OPTIONS *section) {
|
|
char *tmpstr;
|
|
SERVICE_OPTIONS *tmpsrv;
|
|
|
|
/* server mode: update servername_list based on SNI option */
|
|
if(!section->option.client && section->sni) {
|
|
tmpstr=strchr(section->sni, ':');
|
|
if(!tmpstr) {
|
|
section_error(section->servname, "Invalid SNI parameter format");
|
|
return 1;
|
|
}
|
|
*tmpstr++='\0';
|
|
for(tmpsrv=new_service_options.next; tmpsrv; tmpsrv=tmpsrv->next)
|
|
if(!strcmp(tmpsrv->servname, section->sni))
|
|
break;
|
|
if(!tmpsrv) {
|
|
section_error(section->servname, "SNI section name not found");
|
|
return 1;
|
|
}
|
|
if(tmpsrv->option.client) {
|
|
section_error(section->servname, "SNI master service is a TLS client");
|
|
return 1;
|
|
}
|
|
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|=SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
|
|
}
|
|
tmpsrv->servername_list_tail->servername=str_dup(tmpstr);
|
|
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 SSL
|
|
* context settings (including access control) may be different */
|
|
section->ssl_options|=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_name) /* 'connect' option */
|
|
section->sni=str_dup(section->connect_name);
|
|
if(section->sni) { /* either 'protocolHost' or 'connect' specified */
|
|
tmpstr=strrchr(section->sni, ':');
|
|
if(tmpstr) { /* 'host:port' -> drop ':port' */
|
|
*tmpstr='\0';
|
|
} else { /* 'port' -> default to 'localhost' */
|
|
str_free(section->sni);
|
|
section->sni=str_dup("localhost");
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* OPENSSL_NO_TLSEXT */
|
|
|
|
/**************************************** facility/debug level */
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int value;
|
|
} facilitylevel;
|
|
|
|
static int parse_debug_level(char *arg) {
|
|
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 that are not on all unicies */
|
|
#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(strchr(string, '.')) { /* we have a facility specified */
|
|
new_global_options.facility=-1;
|
|
string=strtok(arg_copy, "."); /* break it up */
|
|
|
|
for(fl=facilities; fl->name; ++fl) {
|
|
if(!strcasecmp(fl->name, string)) {
|
|
new_global_options.facility=fl->value;
|
|
break;
|
|
}
|
|
}
|
|
if(new_global_options.facility==-1)
|
|
return 1; /* FAILED */
|
|
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') {
|
|
new_global_options.debug_level=*string-'0';
|
|
return 0; /* OK */
|
|
}
|
|
new_global_options.debug_level=8; /* illegal level */
|
|
for(fl=levels; fl->name; ++fl) {
|
|
if(!strcasecmp(fl->name, string)) {
|
|
new_global_options.debug_level=fl->value;
|
|
break;
|
|
}
|
|
}
|
|
if(new_global_options.debug_level==8)
|
|
return 1; /* FAILED */
|
|
return 0; /* OK */
|
|
}
|
|
|
|
/**************************************** SSL options */
|
|
|
|
static int parse_ssl_option(char *arg) {
|
|
struct {
|
|
char *name;
|
|
long value;
|
|
} 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},
|
|
{"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG},
|
|
{"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER},
|
|
{"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING},
|
|
{"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_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},
|
|
{"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},
|
|
{"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
|
|
{NULL, 0}
|
|
}, *option;
|
|
|
|
for(option=ssl_opts; option->name; ++option)
|
|
if(!strcasecmp(option->name, arg))
|
|
return option->value;
|
|
return 0; /* FAILED */
|
|
}
|
|
|
|
/**************************************** socket options */
|
|
|
|
static int on=1;
|
|
#define DEF_ON ((void *)&on)
|
|
|
|
SOCK_OPT sock_opts[] = {
|
|
{"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
|
|
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, {DEF_ON, NULL, NULL}},
|
|
#ifdef SO_BINDTODEVICE
|
|
{"SO_BINDTODEVICE", SOL_SOCKET, SO_BINDTODEVICE, TYPE_STRING, {NULL, NULL, NULL}},
|
|
#endif
|
|
#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
|
|
#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}},
|
|
{NULL, 0, 0, TYPE_NONE, {NULL, NULL, NULL}}
|
|
};
|
|
|
|
static int print_socket_options(void) {
|
|
int fd;
|
|
socklen_t optlen;
|
|
SOCK_OPT *ptr;
|
|
OPT_UNION val;
|
|
char *ta, *tl, *tr, *td;
|
|
|
|
fd=socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
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)) {
|
|
if(get_last_socket_error()!=S_ENOPROTOOPT) {
|
|
s_log(LOG_ERR, "Failed to get %s OS default", ptr->opt_str);
|
|
sockerror("getsockopt");
|
|
return 1; /* FAILED */
|
|
}
|
|
td=str_dup("write-only");
|
|
} 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, " %-16s|%10s|%10s|%10s|%10s",
|
|
ptr->opt_str, ta, tl, tr, td);
|
|
str_free(ta); str_free(tl); str_free(tr); str_free(td);
|
|
}
|
|
return 0; /* OK */
|
|
}
|
|
|
|
static 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? */
|
|
}
|
|
|
|
static int parse_socket_option(char *arg) {
|
|
int socket_type; /* 0-accept, 1-local, 2-remote */
|
|
char *opt_val_str, *opt_val2_str, *tmpstr;
|
|
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=strtol(opt_val_str, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* 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, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* 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, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* 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=strtol(opt_val2_str, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* 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=strtol(opt_val_str, &tmpstr, 10);
|
|
if(tmpstr==arg || *tmpstr) /* 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 */
|
|
|
|
#ifdef HAVE_OSSL_OCSP_H
|
|
|
|
static char *parse_ocsp_url(SERVICE_OPTIONS *section, char *arg) {
|
|
char *host, *port, *path;
|
|
int ssl;
|
|
|
|
if(!OCSP_parse_url(arg, &host, &port, &path, &ssl))
|
|
return "Failed to parse OCSP URL";
|
|
if(ssl)
|
|
return "SSL not supported for OCSP"
|
|
" - additional stunnel service needs to be defined";
|
|
if(!hostport2addr(§ion->ocsp_addr, host, port))
|
|
return "Failed to resolve OCSP server address";
|
|
section->ocsp_path=str_dup(path);
|
|
if(host)
|
|
OPENSSL_free(host);
|
|
if(port)
|
|
OPENSSL_free(port);
|
|
if(path)
|
|
OPENSSL_free(path);
|
|
return NULL; /* OK! */
|
|
}
|
|
|
|
static 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 /* HAVE_OSSL_OCSP_H */
|
|
|
|
/**************************************** engine */
|
|
|
|
#ifdef HAVE_OSSL_ENGINE_H
|
|
|
|
#define MAX_ENGINES 256
|
|
static ENGINE *engines[MAX_ENGINES]; /* table of engines */
|
|
static int current_engine=0;
|
|
static int engine_initialized;
|
|
|
|
static char *open_engine(const char *name) {
|
|
s_log(LOG_DEBUG, "Enabling support for engine '%s'", name);
|
|
if(!strcasecmp(name, "auto")) {
|
|
ENGINE_register_all_complete();
|
|
s_log(LOG_DEBUG, "Auto engine support enabled");
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
close_engine(); /* close the previous one (if specified) */
|
|
engines[current_engine]=ENGINE_by_id(name);
|
|
engine_initialized=0;
|
|
if(!engines[current_engine]) {
|
|
sslerror("ENGINE_by_id");
|
|
return "Failed to open the engine";
|
|
}
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
static char *ctrl_engine(const char *cmd, const char *arg) {
|
|
if(!strcasecmp(cmd, "INIT")) { /* special control command */
|
|
return init_engine();
|
|
}
|
|
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 */
|
|
}
|
|
|
|
static char *init_engine(void) {
|
|
if(engine_initialized)
|
|
return NULL; /* OK */
|
|
engine_initialized=1;
|
|
s_log(LOG_DEBUG, "Initializing engine %d", current_engine+1);
|
|
if(!ENGINE_init(engines[current_engine])) {
|
|
if(ERR_peek_last_error()) /* really an error */
|
|
sslerror("ENGINE_init");
|
|
else
|
|
s_log(LOG_ERR, "Engine %d not initialized", current_engine+1);
|
|
return "Engine initialization failed";
|
|
}
|
|
if(!ENGINE_set_default(engines[current_engine], ENGINE_METHOD_ALL)) {
|
|
sslerror("ENGINE_set_default");
|
|
return "Selecting default engine failed";
|
|
}
|
|
s_log(LOG_DEBUG, "Engine %d initialized", current_engine+1);
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
static void close_engine(void) {
|
|
if(!engines[current_engine])
|
|
return; /* no engine was opened -> nothing to do */
|
|
init_engine();
|
|
++current_engine;
|
|
#if 0
|
|
ENGINE_finish(e);
|
|
ENGINE_free(e);
|
|
e=NULL;
|
|
s_log(LOG_DEBUG, "Engine closed");
|
|
#endif
|
|
}
|
|
|
|
static ENGINE *get_engine(int i) {
|
|
if(i<1 || i>current_engine)
|
|
return NULL;
|
|
return engines[i-1];
|
|
}
|
|
|
|
#endif /* HAVE_OSSL_ENGINE_H */
|
|
|
|
/**************************************** fatal error */
|
|
|
|
static 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] "
|
|
#endif
|
|
"[-quiet] "
|
|
#endif
|
|
"[<filename>] ] "
|
|
#ifndef USE_WIN32
|
|
"-fd <n> "
|
|
#endif
|
|
"| -help | -version | -sockets");
|
|
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");
|
|
#endif
|
|
s_log(LOG_NOTICE, " -quiet - don't display a message box on success");
|
|
#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");
|
|
}
|
|
|
|
/**************************************** various supporting functions */
|
|
|
|
static void config_error(int num, const char *line, const char *str) {
|
|
s_log(LOG_ERR, "Line %d: \"%s\": %s", num, line, str);
|
|
}
|
|
|
|
static void section_error(const char *name, const char *str) {
|
|
s_log(LOG_ERR, "Section %s: %s", name, str);
|
|
}
|
|
|
|
#ifndef USE_WIN32
|
|
|
|
static char **argalloc(char *str) { /* allocate 'exec' argumets */
|
|
int 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 */
|