--- a/doc/stunnel.8 +++ b/doc/stunnel.8 @@ -497,7 +497,10 @@ time to keep an idle connection .IP "\fBtransparent\fR = yes | no (Unix only)" 4 .IX Item "transparent = yes | no (Unix only)" transparent proxy mode -.Sp +.IP "\fBxforwardedfor\fR = yes | no" 4 +.IX Item "xforwardedfor = yes | no" +append an 'X-Forwarded-For:' HTTP request header providing the +client's IP address to the server. Re-write address to appear as if wrapped daemon is connecting from the \s-1SSL\s0 client machine instead of the machine running \fBstunnel\fR. .Sp --- /dev/null +++ b/doc/stunnel.8.orig @@ -0,0 +1,729 @@ +.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" ======================================================================== +.\" +.IX Title "STUNNEL 8" +.TH STUNNEL 8 "2009.11.20" "4.29" "stunnel" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +stunnel \- universal SSL tunnel +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.IP "\fBUnix:\fR" 4 +.IX Item "Unix:" +\&\fBstunnel\fR [] | \-fd n | \-help | \-version | \-sockets +.IP "\fB\s-1WIN32:\s0\fR" 4 +.IX Item "WIN32:" +\&\fBstunnel\fR [ [\-install | \-uninstall | \-start | \-stop] + [\-quiet] [] ] | \-help | \-version | \-sockets +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +The \fBstunnel\fR program is designed to work as \fI\s-1SSL\s0\fR encryption wrapper +between remote clients and local (\fIinetd\fR\-startable) or remote +servers. The concept is that having non-SSL aware daemons running on +your system you can easily set them up to communicate with clients over +secure \s-1SSL\s0 channels. +.PP +\&\fBstunnel\fR can be used to add \s-1SSL\s0 functionality to commonly used \fIInetd\fR +daemons like \s-1POP\-2\s0, \s-1POP\-3\s0, and \s-1IMAP\s0 servers, to standalone daemons like +\&\s-1NNTP\s0, \s-1SMTP\s0 and \s-1HTTP\s0, and in tunneling \s-1PPP\s0 over network sockets without +changes to the source code. +.PP +This product includes cryptographic software written by +Eric Young (eay@cryptsoft.com) +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "<\fBfilename\fR>" 4 +.IX Item "" +Use specified configuration file +.IP "\fB\-fd n\fR (Unix only)" 4 +.IX Item "-fd n (Unix only)" +Read the config file from specified file descriptor +.IP "\fB\-help\fR" 4 +.IX Item "-help" +Print \fBstunnel\fR help menu +.IP "\fB\-version\fR" 4 +.IX Item "-version" +Print \fBstunnel\fR version and compile time defaults +.IP "\fB\-sockets\fR" 4 +.IX Item "-sockets" +Print default socket options +.IP "\fB\-install\fR (\s-1NT/2000/XP\s0 only)" 4 +.IX Item "-install (NT/2000/XP only)" +Install \s-1NT\s0 Service +.IP "\fB\-uninstall\fR (\s-1NT/2000/XP\s0 only)" 4 +.IX Item "-uninstall (NT/2000/XP only)" +Uninstall \s-1NT\s0 Service +.IP "\fB\-start\fR (\s-1NT/2000/XP\s0 only)" 4 +.IX Item "-start (NT/2000/XP only)" +Start \s-1NT\s0 Service +.IP "\fB\-stop\fR (\s-1NT/2000/XP\s0 only)" 4 +.IX Item "-stop (NT/2000/XP only)" +Stop \s-1NT\s0 Service +.IP "\fB\-quiet\fR (\s-1NT/2000/XP\s0 only)" 4 +.IX Item "-quiet (NT/2000/XP only)" +Don't display a message box when successfully installed or uninstalled \s-1NT\s0 service +.SH "CONFIGURATION FILE" +.IX Header "CONFIGURATION FILE" +Each line of the configuration file can be either: +.IP "\(bu" 4 +an empty line (ignored) +.IP "\(bu" 4 +a comment starting with ';' (ignored) +.IP "\(bu" 4 +an 'option_name = option_value' pair +.IP "\(bu" 4 +\&'[service_name]' indicating a start of a service definition +.SS "\s-1GLOBAL\s0 \s-1OPTIONS\s0" +.IX Subsection "GLOBAL OPTIONS" +.IP "\fBchroot\fR = directory (Unix only)" 4 +.IX Item "chroot = directory (Unix only)" +directory to chroot \fBstunnel\fR process +.Sp +\&\fBchroot\fR keeps \fBstunnel\fR in chrooted jail. \fICApath\fR, \fICRLpath\fR, \fIpid\fR +and \fIexec\fR are located inside the jail and the patches have to be relative +to the directory specified with \fBchroot\fR. +.Sp +To have libwrap (\s-1TCP\s0 Wrappers) control effective in a chrooted environment +you also have to copy its configuration files (/etc/hosts.allow and +/etc/hosts.deny) there. +.IP "\fBcompression\fR = zlib | rle" 4 +.IX Item "compression = zlib | rle" +select data compression algorithm +.Sp +default: no compression +.Sp +zlib compression of OpenSSL 0.9.8 or above is not backward compatible with +OpenSSL 0.9.7. +.Sp +rle compression is currently not implemented by the OpenSSL library. +.IP "\fBdebug\fR = [facility.]level" 4 +.IX Item "debug = [facility.]level" +debugging level +.Sp +Level is a one of the syslog level names or numbers +emerg (0), alert (1), crit (2), err (3), warning (4), notice (5), +info (6), or debug (7). All logs for the specified level and +all levels numerically less than it will be shown. Use \fBdebug = debug\fR or +\&\fBdebug = 7\fR for greatest debugging output. The default is notice (5). +.Sp +The syslog facility 'daemon' will be used unless a facility name is supplied. +(Facilities are not supported on Win32.) +.Sp +Case is ignored for both facilities and levels. +.IP "\fB\s-1EGD\s0\fR = egd path (Unix only)" 4 +.IX Item "EGD = egd path (Unix only)" +path to Entropy Gathering Daemon socket +.Sp +Entropy Gathering Daemon socket to use to feed OpenSSL random number +generator. (Available only if compiled with OpenSSL 0.9.5a or higher) +.IP "\fBengine\fR = auto | " 4 +.IX Item "engine = auto | " +select hardware engine +.Sp +default: software-only cryptography +.Sp +There's an example in '\s-1EXAMPLES\s0' section. +.IP "\fBengineCtrl\fR = command[:parameter]" 4 +.IX Item "engineCtrl = command[:parameter]" +control hardware engine +.Sp +Special commands \*(L"\s-1LOAD\s0\*(R" and \*(L"\s-1INIT\s0\*(R" can be used to load and initialize the +engine cryptogaphic module. +.IP "\fBfips\fR = yes | no" 4 +.IX Item "fips = yes | no" +Enable or disable \s-1FIPS\s0 140\-2 mode. +.Sp +This option allows to disable entering \s-1FIPS\s0 mode if stunnel was compiled with +\&\s-1FIPS\s0 140\-2 support. +.Sp +default: yes +.IP "\fBforeground\fR = yes | no (Unix only)" 4 +.IX Item "foreground = yes | no (Unix only)" +foreground mode +.Sp +Stay in foreground (don't fork) and log to stderr +instead of via syslog (unless \fBoutput\fR is specified). +.Sp +default: background in daemon mode +.IP "\fBoutput\fR = file" 4 +.IX Item "output = file" +append log messages to a file instead of using syslog +.Sp +/dev/stdout device can be used to redirect log messages to the standard +output (for example to log them with daemontools splogger). +.IP "\fBpid\fR = file (Unix only)" 4 +.IX Item "pid = file (Unix only)" +pid file location +.Sp +If the argument is empty, then no pid file will be created. +.Sp +\&\fIpid\fR path is relative to \fIchroot\fR directory if specified. +.IP "\fBRNDbytes\fR = bytes" 4 +.IX Item "RNDbytes = bytes" +bytes to read from random seed files +.Sp +Number of bytes of data read from random seed files. With \s-1SSL\s0 versions +less than 0.9.5a, also determines how many bytes of data are considered +sufficient to seed the \s-1PRNG\s0. More recent OpenSSL versions have a builtin +function to determine when sufficient randomness is available. +.IP "\fBRNDfile\fR = file" 4 +.IX Item "RNDfile = file" +path to file with random seed data +.Sp +The \s-1SSL\s0 library will use data from this file first to seed the random +number generator. +.IP "\fBRNDoverwrite\fR = yes | no" 4 +.IX Item "RNDoverwrite = yes | no" +overwrite the random seed files with new random data +.Sp +default: yes +.IP "\fBservice\fR = servicename" 4 +.IX Item "service = servicename" +use specified string as the service name +.Sp +\&\fBOn Unix:\fR \fIinetd\fR mode service name for \s-1TCP\s0 Wrapper library. +.Sp +\&\fBOn \s-1NT/2000/XP:\s0\fR \s-1NT\s0 service name in the Control Panel. +.Sp +default: stunnel +.IP "\fBsetgid\fR = groupname (Unix only)" 4 +.IX Item "setgid = groupname (Unix only)" +\&\fIsetgid()\fR to groupname in daemon mode and clears all other groups +.IP "\fBsetuid\fR = username (Unix only)" 4 +.IX Item "setuid = username (Unix only)" +\&\fIsetuid()\fR to username in daemon mode +.IP "\fBsocket\fR = a|l|r:option=value[:value]" 4 +.IX Item "socket = a|l|r:option=value[:value]" +Set an option on accept/local/remote socket +.Sp +The values for linger option are l_onof:l_linger. +The values for time are tv_sec:tv_usec. +.Sp +Examples: +.Sp +.Vb 11 +\& socket = l:SO_LINGER=1:60 +\& set one minute timeout for closing local socket +\& socket = r:TCP_NODELAY=1 +\& turn off the Nagle algorithm for remote sockets +\& socket = r:SO_OOBINLINE=1 +\& place out\-of\-band data directly into the +\& receive data stream for remote sockets +\& socket = a:SO_REUSEADDR=0 +\& disable address reuse (enabled by default) +\& socket = a:SO_BINDTODEVICE=lo +\& only accept connections on loopback interface +.Ve +.IP "\fBsyslog\fR = yes | no (Unix only)" 4 +.IX Item "syslog = yes | no (Unix only)" +enable logging via syslog +.Sp +default: yes +.IP "\fBtaskbar\fR = yes | no (\s-1WIN32\s0 only)" 4 +.IX Item "taskbar = yes | no (WIN32 only)" +enable the taskbar icon +.Sp +default: yes +.SS "SERVICE-LEVEL \s-1OPTIONS\s0" +.IX Subsection "SERVICE-LEVEL OPTIONS" +Each configuration section begins with service name in square brackets. +The service name is used for libwrap (\s-1TCP\s0 Wrappers) access control and lets +you distinguish \fBstunnel\fR services in your log files. +.PP +Note that if you wish to run \fBstunnel\fR in \fIinetd\fR mode (where it +is provided a network socket by a server such as \fIinetd\fR, \fIxinetd\fR, +or \fItcpserver\fR) then you should read the section entitled \fI\s-1INETD\s0 \s-1MODE\s0\fR +below. +.IP "\fBaccept\fR = [host:]port" 4 +.IX Item "accept = [host:]port" +accept connections on specified host:port +.Sp +If no host specified, defaults to all \s-1IP\s0 addresses for the local host. +.IP "\fBCApath\fR = directory" 4 +.IX Item "CApath = directory" +Certificate Authority directory +.Sp +This is the directory in which \fBstunnel\fR will look for certificates when using +the \fIverify\fR. Note that the certificates in this directory should be named +\&\s-1XXXXXXXX\s0.0 where \s-1XXXXXXXX\s0 is the hash value of the \s-1DER\s0 encoded subject of the +cert (the first 4 bytes of the \s-1MD5\s0 hash in least significant byte order). +.Sp +\&\fICApath\fR path is relative to \fIchroot\fR directory if specified. +.IP "\fBCAfile\fR = certfile" 4 +.IX Item "CAfile = certfile" +Certificate Authority file +.Sp +This file contains multiple \s-1CA\s0 certificates, used with the \fIverify\fR. +.IP "\fBcert\fR = pemfile" 4 +.IX Item "cert = pemfile" +certificate chain \s-1PEM\s0 file name +.Sp +A \s-1PEM\s0 is always needed in server mode. +Specifying this flag in client mode will use this certificate chain +as a client side certificate chain. Using client side certs is optional. +The certificates must be in \s-1PEM\s0 format and must be sorted starting with the +certificate to the highest level (root \s-1CA\s0). +.IP "\fBciphers\fR = cipherlist" 4 +.IX Item "ciphers = cipherlist" +Select permitted \s-1SSL\s0 ciphers +.Sp +A colon delimited list of the ciphers to allow in the \s-1SSL\s0 connection. +For example \s-1DES\-CBC3\-SHA:IDEA\-CBC\-MD5\s0 +.IP "\fBclient\fR = yes | no" 4 +.IX Item "client = yes | no" +client mode (remote service uses \s-1SSL\s0) +.Sp +default: no (server mode) +.IP "\fBconnect\fR = [host:]port" 4 +.IX Item "connect = [host:]port" +connect to a remote host:port +.Sp +If no host is specified, the host defaults to localhost. +.Sp +Multiple \fBconnect\fR options are allowed in a single service section. +.Sp +If host resolves to multiple addresses and/or if multiple \fBconnect\fR +options are specified, then the remote address is chosen using a +round-robin algorithm. +.IP "\fBCRLpath\fR = directory" 4 +.IX Item "CRLpath = directory" +Certificate Revocation Lists directory +.Sp +This is the directory in which \fBstunnel\fR will look for CRLs when +using the \fIverify\fR. Note that the CRLs in this directory should +be named \s-1XXXXXXXX\s0.0 where \s-1XXXXXXXX\s0 is the hash value of the \s-1CRL\s0. +.Sp +\&\fICRLpath\fR path is relative to \fIchroot\fR directory if specified. +.IP "\fBCRLfile\fR = certfile" 4 +.IX Item "CRLfile = certfile" +Certificate Revocation Lists file +.Sp +This file contains multiple CRLs, used with the \fIverify\fR. +.IP "\fBdelay\fR = yes | no" 4 +.IX Item "delay = yes | no" +delay \s-1DNS\s0 lookup for 'connect' option +.IP "\fBengineNum\fR = engine number" 4 +.IX Item "engineNum = engine number" +select engine number to read private key +.Sp +The engines are numbered starting from 1. +.IP "\fBexec\fR = executable_path (Unix only)" 4 +.IX Item "exec = executable_path (Unix only)" +execute local inetd-type program +.Sp +\&\fIexec\fR path is relative to \fIchroot\fR directory if specified. +.ie n .IP "\fBexecargs\fR = $0 $1 $2 ... (Unix only)" 4 +.el .IP "\fBexecargs\fR = \f(CW$0\fR \f(CW$1\fR \f(CW$2\fR ... (Unix only)" 4 +.IX Item "execargs = $0 $1 $2 ... (Unix only)" +arguments for \fIexec\fR including program name ($0) +.Sp +Quoting is currently not supported. +Arguments are separated with arbitrary number of whitespaces. +.IP "\fBfailover\fR = rr | prio" 4 +.IX Item "failover = rr | prio" +Failover strategy for multiple \*(L"connect\*(R" targets. +.Sp +.Vb 2 +\& rr (round robin) \- fair load distribution +\& prio (priority) \- use the order specified in config file +.Ve +.Sp +default: rr +.IP "\fBident\fR = username" 4 +.IX Item "ident = username" +use \s-1IDENT\s0 (\s-1RFC\s0 1413) username checking +.IP "\fBkey\fR = keyfile" 4 +.IX Item "key = keyfile" +private key for certificate specified with \fIcert\fR option +.Sp +Private key is needed to authenticate certificate owner. +Since this file should be kept secret it should only be readable +to its owner. On Unix systems you can use the following command: +.Sp +.Vb 1 +\& chmod 600 keyfile +.Ve +.Sp +default: value of \fIcert\fR option +.IP "\fBlocal\fR = host" 4 +.IX Item "local = host" +\&\s-1IP\s0 of the outgoing interface is used as source for remote connections. +Use this option to bind a static local \s-1IP\s0 address, instead. +.IP "\fB\s-1OCSP\s0\fR = url" 4 +.IX Item "OCSP = url" +select \s-1OCSP\s0 server for certificate verification +.IP "\fBOCSPflag\fR = flag" 4 +.IX Item "OCSPflag = flag" +specify \s-1OCSP\s0 server flag +.Sp +Several \fIOCSPflag\fR can be used to specify multiple flags. +.Sp +currently supported flags: \s-1NOCERTS\s0, \s-1NOINTERN\s0 \s-1NOSIGS\s0, \s-1NOCHAIN\s0, \s-1NOVERIFY\s0, +\&\s-1NOEXPLICIT\s0, \s-1NOCASIGN\s0, \s-1NODELEGATED\s0, \s-1NOCHECKS\s0, \s-1TRUSTOTHER\s0, \s-1RESPID_KEY\s0, \s-1NOTIME\s0 +.IP "\fBoptions\fR = SSL_options" 4 +.IX Item "options = SSL_options" +OpenSSL library options +.Sp +The parameter is the OpenSSL option name as described in the +\&\fI\fISSL_CTX_set_options\fI\|(3ssl)\fR manual, but without \fI\s-1SSL_OP_\s0\fR prefix. +Several \fIoptions\fR can be used to specify multiple options. +.Sp +For example for compatibility with erroneous Eudora \s-1SSL\s0 implementation +the following option can be used: +.Sp +.Vb 1 +\& options = DONT_INSERT_EMPTY_FRAGMENTS +.Ve +.IP "\fBprotocol\fR = proto" 4 +.IX Item "protocol = proto" +application protocol to negotiate \s-1SSL\s0 +.Sp +currently supported: cifs, connect, imap, nntp, pop3, smtp, pgsql +.IP "\fBprotocolAuthentication\fR = auth_type" 4 +.IX Item "protocolAuthentication = auth_type" +authentication type for protocol negotiations +.Sp +currently supported: basic, \s-1NTLM\s0 +.Sp +Currently authentication type only applies to 'connect' protocol. +.Sp +default: basic +.IP "\fBprotocolHost\fR = host:port" 4 +.IX Item "protocolHost = host:port" +destination address for protocol negotiations +.IP "\fBprotocolPassword\fR = password" 4 +.IX Item "protocolPassword = password" +password for protocol negotiations +.IP "\fBprotocolUsername\fR = username" 4 +.IX Item "protocolUsername = username" +username for protocol negotiations +.IP "\fBpty\fR = yes | no (Unix only)" 4 +.IX Item "pty = yes | no (Unix only)" +allocate pseudo terminal for 'exec' option +.IP "\fBretry\fR = yes | no (Unix only)" 4 +.IX Item "retry = yes | no (Unix only)" +reconnect a connect+exec section after it's disconnected +.Sp +default: no +.IP "\fBsession\fR = timeout" 4 +.IX Item "session = timeout" +session cache timeout +.IP "\fBsessiond\fR = host:port" 4 +.IX Item "sessiond = host:port" +address of sessiond \s-1SSL\s0 cache server +.IP "\fBsslVersion\fR = version" 4 +.IX Item "sslVersion = version" +select version of \s-1SSL\s0 protocol +.Sp +Allowed options: all, SSLv2, SSLv3, TLSv1 +.IP "\fBstack\fR = bytes (except for \s-1FORK\s0 model)" 4 +.IX Item "stack = bytes (except for FORK model)" +thread stack size +.IP "\fBTIMEOUTbusy\fR = seconds" 4 +.IX Item "TIMEOUTbusy = seconds" +time to wait for expected data +.IP "\fBTIMEOUTclose\fR = seconds" 4 +.IX Item "TIMEOUTclose = seconds" +time to wait for close_notify (set to 0 for buggy \s-1MSIE\s0) +.IP "\fBTIMEOUTconnect\fR = seconds" 4 +.IX Item "TIMEOUTconnect = seconds" +time to wait to connect a remote host +.IP "\fBTIMEOUTidle\fR = seconds" 4 +.IX Item "TIMEOUTidle = seconds" +time to keep an idle connection +.IP "\fBtransparent\fR = yes | no (Unix only)" 4 +.IX Item "transparent = yes | no (Unix only)" +transparent proxy mode +.Sp +Re-write address to appear as if wrapped daemon is connecting +from the \s-1SSL\s0 client machine instead of the machine running \fBstunnel\fR. +.Sp +This option is currently available in: +.Sp +.Vb 3 +\& remote mode (I option) on Linux >=2.6.28 +\& remote mode (I option) 2.2.x +\& local mode (I option) +.Ve +.Sp +\&\fBRemote mode\fR (either 2.2.x and >=2.6.28) requires stunnel to be executed as +root. \fBsetuid\fR option will also break this functionality. +.Sp +\&\fBLinux >=2.6.28\fR requires the following setup for iptables and routing +(possibly in /etc/rc.local or equivalent file): +.Sp +.Vb 6 +\& iptables \-t mangle \-N DIVERT +\& iptables \-t mangle \-A PREROUTING \-p tcp \-m socket \-j DIVERT +\& iptables \-t mangle \-A DIVERT \-j MARK \-\-set\-mark 1 +\& iptables \-t mangle \-A DIVERT \-j ACCEPT +\& ip rule add fwmark 1 lookup 100 +\& ip route add local 0.0.0.0/0 dev lo table 100 +.Ve +.Sp +\&\fBLinux 2.2.x\fR requires kernel to be compiled with \fItransparent proxy\fR option. +Connected service must be installed on a separate host. +Routing towards the clients has to go through the stunnel box. +.Sp +\&\fBLocal mode\fR works by LD_PRELOADing env.so shared library. +.IP "\fBverify\fR = level" 4 +.IX Item "verify = level" +verify peer certificate +.Sp +.Vb 4 +\& level 1 \- verify peer certificate if present +\& level 2 \- verify peer certificate +\& level 3 \- verify peer with locally installed certificate +\& default \- no verify +.Ve +.Sp +It is important to understand, that this option was solely designed for access +control and not for authorization. Specifically for level 2 every non-revoked +certificate is accepted regardless of its Common Name. For this reason a +dedicated \s-1CA\s0 should be used with level 2, and not a generic \s-1CA\s0 commonly used +for webservers. Level 3 is preferred for point-to-point connections. +.SH "RETURN VALUE" +.IX Header "RETURN VALUE" +\&\fBstunnel\fR returns zero on success, non-zero on error. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +In order to provide \s-1SSL\s0 encapsulation to your local \fIimapd\fR service, use +.PP +.Vb 4 +\& [imapd] +\& accept = 993 +\& exec = /usr/sbin/imapd +\& execargs = imapd +.Ve +.PP +If you want to provide tunneling to your \fIpppd\fR daemon on port 2020, +use something like +.PP +.Vb 5 +\& [vpn] +\& accept = 2020 +\& exec = /usr/sbin/pppd +\& execargs = pppd local +\& pty = yes +.Ve +.PP +If you want to use \fBstunnel\fR in \fIinetd\fR mode to launch your imapd +process, you'd use this \fIstunnel.conf\fR. +Note there must be no \fI[service_name]\fR section. +.PP +.Vb 2 +\& exec = /usr/sbin/imapd +\& execargs = imapd +.Ve +.PP +Here is an example of advanced engine configuration to read private key from an +OpenSC engine +.PP +.Vb 7 +\& engine=dynamic +\& engineCtrl=SO_PATH:/usr/lib/opensc/engine_pkcs11.so +\& engineCtrl=ID:pkcs11 +\& engineCtrl=LIST_ADD:1 +\& engineCtrl=LOAD +\& engineCtrl=MODULE_PATH:/usr/lib/pkcs11/opensc\-pkcs11.so +\& engineCtrl=INIT +\& +\& [service] +\& engineNum=1 +\& key=id_45 +.Ve +.SH "FILES" +.IX Header "FILES" +.IP "\fIstunnel.conf\fR" 4 +.IX Item "stunnel.conf" +\&\fBstunnel\fR configuration file +.IP "\fIstunnel.pem\fR" 4 +.IX Item "stunnel.pem" +\&\fBstunnel\fR certificate and private key +.SH "BUGS" +.IX Header "BUGS" +Option \fIexecargs\fR does not support quoting. +.SH "RESTRICTIONS" +.IX Header "RESTRICTIONS" +\&\fBstunnel\fR cannot be used for the \s-1FTP\s0 daemon because of the nature +of the \s-1FTP\s0 protocol which utilizes multiple ports for data transfers. +There are available \s-1SSL\s0 enabled versions of \s-1FTP\s0 and telnet daemons, however. +.SH "NOTES" +.IX Header "NOTES" +.SS "\s-1INETD\s0 \s-1MODE\s0" +.IX Subsection "INETD MODE" +The most common use of \fBstunnel\fR is to listen on a network +port and establish communication with either a new port +via the connect option, or a new program via the \fIexec\fR option. +However there is a special case when you wish to have +some other program accept incoming connections and +launch \fBstunnel\fR, for example with \fIinetd\fR, \fIxinetd\fR, +or \fItcpserver\fR. +.PP +For example, if you have the following line in \fIinetd.conf\fR: +.PP +.Vb 1 +\& imaps stream tcp nowait root /usr/bin/stunnel stunnel /etc/stunnel/imaps.conf +.Ve +.PP +In these cases, the \fIinetd\fR\-style program is responsible +for binding a network socket (\fIimaps\fR above) and handing +it to \fBstunnel\fR when a connection is received. +Thus you do not want \fBstunnel\fR to have any \fIaccept\fR option. +All the \fIService Level Options\fR should be placed in the +global options section, and no \fI[service_name]\fR section +will be present. See the \fI\s-1EXAMPLES\s0\fR section for example +configurations. +.SS "\s-1CERTIFICATES\s0" +.IX Subsection "CERTIFICATES" +Each \s-1SSL\s0 enabled daemon needs to present a valid X.509 certificate +to the peer. It also needs a private key to decrypt the incoming +data. The easiest way to obtain a certificate and a key is to +generate them with the free \fIOpenSSL\fR package. You can find more +information on certificates generation on pages listed below. +.PP +The order of contents of the \fI.pem\fR file is important. It should contain the +unencrypted private key first, then a signed certificate (not certificate +request). There should be also empty lines after certificate and private key. +Plaintext certificate information appended on the top of generated certificate +should be discarded. So the file should look like this: +.PP +.Vb 8 +\& \-\-\-\-\-BEGIN RSA PRIVATE KEY\-\-\-\-\- +\& [encoded key] +\& \-\-\-\-\-END RSA PRIVATE KEY\-\-\-\-\- +\& [empty line] +\& \-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\- +\& [encoded certificate] +\& \-\-\-\-\-END CERTIFICATE\-\-\-\-\- +\& [empty line] +.Ve +.SS "\s-1RANDOMNESS\s0" +.IX Subsection "RANDOMNESS" +\&\fBstunnel\fR needs to seed the \s-1PRNG\s0 (pseudo random number generator) in +order for \s-1SSL\s0 to use good randomness. The following sources are loaded +in order until sufficient random data has been gathered: +.IP "\(bu" 4 +The file specified with the \fIRNDfile\fR flag. +.IP "\(bu" 4 +The file specified by the \s-1RANDFILE\s0 environment variable, if set. +.IP "\(bu" 4 +The file .rnd in your home directory, if \s-1RANDFILE\s0 not set. +.IP "\(bu" 4 +The file specified with '\-\-with\-random' at compile time. +.IP "\(bu" 4 +The contents of the screen if running on Windows. +.IP "\(bu" 4 +The egd socket specified with the \fI\s-1EGD\s0\fR flag. +.IP "\(bu" 4 +The egd socket specified with '\-\-with\-egd\-sock' at compile time. +.IP "\(bu" 4 +The /dev/urandom device. +.PP +With recent (>=OpenSSL 0.9.5a) version of \s-1SSL\s0 it will stop loading +random data automatically when sufficient entropy has been gathered. +With previous versions it will continue to gather from all the above +sources since no \s-1SSL\s0 function exists to tell when enough data is available. +.PP +Note that on Windows machines that do not have console user interaction +(mouse movements, creating windows, etc) the screen contents are not +variable enough to be sufficient, and you should provide a random file +for use with the \fIRNDfile\fR flag. +.PP +Note that the file specified with the \fIRNDfile\fR flag should contain +random data \*(-- that means it should contain different information +each time \fBstunnel\fR is run. This is handled automatically +unless the \fIRNDoverwrite\fR flag is used. If you wish to update this file +manually, the \fIopenssl rand\fR command in recent versions of OpenSSL, +would be useful. +.PP +One important note \*(-- if /dev/urandom is available, OpenSSL has a habit of +seeding the \s-1PRNG\s0 with it even when checking the random state, so on +systems with /dev/urandom you're likely to use it even though it's listed +at the very bottom of the list above. This isn't \fBstunnel's\fR behaviour, it's +OpenSSLs. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +.IP "\fItcpd\fR\|(8)" 4 +.IX Item "tcpd" +access control facility for internet services +.IP "\fIinetd\fR\|(8)" 4 +.IX Item "inetd" +internet 'super\-server' +.IP "\fIhttp://stunnel.mirt.net/\fR" 4 +.IX Item "http://stunnel.mirt.net/" +\&\fBstunnel\fR homepage +.IP "\fIhttp://www.stunnel.org/\fR" 4 +.IX Item "http://www.stunnel.org/" +\&\fBstunnel\fR Frequently Asked Questions +.IP "\fIhttp://www.openssl.org/\fR" 4 +.IX Item "http://www.openssl.org/" +OpenSSL project website +.SH "AUTHOR" +.IX Header "AUTHOR" +.IP "Michal Trojnara" 4 +.IX Item "Michal Trojnara" +<\fIMichal.Trojnara@mirt.net\fR> --- a/doc/stunnel.fr.8 +++ b/doc/stunnel.fr.8 @@ -445,6 +445,10 @@ Cette option permet de relier une adress Négocie avec \s-1SSL\s0 selon le protocole indiqué .Sp Actuellement gérés\ : cifs, nntp, pop3, smtp +.IP "\fBxforwardedfor\fR = yes | no" 4 +.IX Item "xforwardedfor = yes | no" +Ajoute un en-tête 'X-Forwarded-For:' dans la requête HTTP fournissant +au serveur l'adresse IP du client. .IP "\fBpty\fR = yes | no (Unix seulement)" 4 .IX Item "pty = yes | no (Unix seulement)" Alloue un pseudo-terminal pour l'option «\ exec\ » --- a/src/client.c +++ b/src/client.c @@ -90,6 +90,12 @@ CLI *alloc_client_session(LOCAL_OPTIONS return NULL; } c->opt=opt; + /* some options need space to add some information */ + if (c->opt->option.xforwardedfor) + c->buffsize = BUFFSIZE - BUFF_RESERVED; + else + c->buffsize = BUFFSIZE; + c->crlf_seen=0; c->local_rfd.fd=rfd; c->local_wfd.fd=wfd; return c; @@ -382,6 +388,29 @@ static void init_ssl(CLI *c) { } } +/* Moves all data from the buffer between positions and + * to insert of length . and are updated to their + * new respective values, and the number of characters inserted is returned. + * If is too long, nothing is done and -1 is returned. + * Note that neither nor can be NULL. +*/ +static int buffer_insert_with_len(char *buffer, int *start, int *stop, int limit, char *string, int len) { + if (len > limit - *stop) + return -1; + if (*start > *stop) + return -1; + memmove(buffer + *start + len, buffer + *start, *stop - *start); + memcpy(buffer + *start, string, len); + *start += len; + *stop += len; + return len; +} + +static int buffer_insert(char *buffer, int *start, int *stop, int limit, char *string) { + return buffer_insert_with_len(buffer, start, stop, limit, string, strlen(string)); +} + + /****************************** some defines for transfer() */ /* is socket/SSL open for read/write? */ #define sock_rd (c->sock_rfd->rd) @@ -416,13 +445,16 @@ static void transfer(CLI *c) { check_SSL_pending=0; SSL_read_wants_read= - ssl_rd && c->ssl_ptrssl_ptrssl_ptrbuffsize && !SSL_read_wants_write; + SSL_write_wants_write= ssl_wr && c->sock_ptr && !SSL_write_wants_read; /****************************** setup c->fds structure */ s_poll_init(&c->fds); /* initialize the structure */ - if(sock_rd && c->sock_ptrsock_ptrsock_ptrbuffsize) s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0); if(SSL_read_wants_read || SSL_write_wants_read || @@ -521,7 +553,8 @@ static void transfer(CLI *c) { break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); - if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ + //if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ + if(c->ssl_ptr>=c->buffsize) /* buffer was previously full */ check_SSL_pending=1; /* check for data buffered by SSL */ c->ssl_ptr-=num; c->sock_bytes+=num; @@ -581,7 +614,8 @@ static void transfer(CLI *c) { /****************************** read from socket */ if(sock_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, - c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); + //c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); + c->sock_buff+c->sock_ptr, c->buffsize-c->sock_ptr); switch(num) { case -1: parse_socket_error(c, "readsocket"); @@ -601,10 +635,73 @@ static void transfer(CLI *c) { (SSL_read_wants_write && ssl_can_wr) || (check_SSL_pending && SSL_pending(c->ssl))) { SSL_read_wants_write=0; - num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); + //num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); + num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, c->buffsize-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: + //c->ssl_ptr+=num; + if (c->buffsize != BUFFSIZE && c->opt->option.xforwardedfor) { /* some work left to do */ + int last = c->ssl_ptr; + c->ssl_ptr += num; + + /* Look for end of HTTP headers between last and ssl_ptr. + * To achieve this reliably, we have to count the number of + * successive [CR]LF and to memorize it in case it's spread + * over multiple segments. --WT. + */ + while (last < c->ssl_ptr) { + if (c->ssl_buff[last] == '\n') { + if (++c->crlf_seen == 2) + break; + } else if (last < c->ssl_ptr - 1 && + c->ssl_buff[last] == '\r' && + c->ssl_buff[last+1] == '\n') { + if (++c->crlf_seen == 2) + break; + last++; + } else if (c->ssl_buff[last] != '\r') + /* don't refuse '\r' because we may get a '\n' on next read */ + c->crlf_seen = 0; + last++; + } + if (c->crlf_seen >= 2) { + /* We have all the HTTP headers now. We don't need to + * reserve any space anymore. points to the + * first byte of unread data, and points to the + * exact location where we want to insert our headers, + * which is right before the empty line. + */ + c->buffsize = BUFFSIZE; + + if (c->opt->option.xforwardedfor) { + /* X-Forwarded-For: xxxx \r\n\0 */ + char xforw[17 + IPLEN + 3]; + + /* We will insert our X-Forwarded-For: header here. + * We need to write the IP address, but if we use + * sprintf, it will pad with the terminating 0. + * So we will pass via a temporary buffer allocated + * on the stack. + */ + memcpy(xforw, "X-Forwarded-For: ", 17); + if (getnameinfo(&c->peer_addr.addr[0].sa, + addr_len(c->peer_addr.addr[0]), + xforw + 17, IPLEN, NULL, 0, + NI_NUMERICHOST) == 0) { + strcat(xforw + 17, "\r\n"); + buffer_insert(c->ssl_buff, &last, &c->ssl_ptr, + c->buffsize, xforw); + } + /* last still points to the \r\n and ssl_ptr to the + * end of the buffer, so we may add as many headers + * as wee need to. + */ + } + } + } + else c->ssl_ptr+=num; + watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: --- /dev/null +++ b/src/client.c.orig @@ -0,0 +1,1042 @@ +/* + * stunnel Universal SSL tunnel + * Copyright (C) 1998-2009 Michal Trojnara + * + * 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 . + * + * 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. + */ + +/* Undefine if you have problems with make_sockets() */ +#define INET_SOCKET_PAIR + +#include "common.h" +#include "prototypes.h" + +#ifndef SHUT_RD +#define SHUT_RD 0 +#endif +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif + +#if SSLEAY_VERSION_NUMBER >= 0x0922 +static char *sid_ctx="stunnel SID"; + /* const allowed here */ +#endif + +static void do_client(CLI *); +static void run_client(CLI *); +static void init_local(CLI *); +static void init_remote(CLI *); +static void init_ssl(CLI *); +static void transfer(CLI *); +static void parse_socket_error(CLI *, const char *); + +static void print_cipher(CLI *); +static void auth_user(CLI *); +static int connect_local(CLI *); +#ifndef USE_WIN32 +static void make_sockets(CLI *, int [2]); +#endif +static int connect_remote(CLI *); +static void local_bind(CLI *c); +static void print_bound_address(CLI *); +static void reset(int, char *); + +int max_clients; +#ifndef USE_WIN32 +int max_fds; +#endif + +/* Allocate local data structure for the new thread */ +CLI *alloc_client_session(LOCAL_OPTIONS *opt, int rfd, int wfd) { + CLI *c; + + c=calloc(1, sizeof(CLI)); + if(!c) { + s_log(LOG_ERR, "Memory allocation failed"); + return NULL; + } + c->opt=opt; + c->local_rfd.fd=rfd; + c->local_wfd.fd=wfd; + return c; +} + +void *client(void *arg) { + CLI *c=arg; + +#ifdef DEBUG_STACK_SIZE + stack_info(1); /* initialize */ +#endif + s_log(LOG_DEBUG, "%s started", c->opt->servname); +#ifndef USE_WIN32 + if(c->opt->option.remote && c->opt->option.program) { + /* connect and exec options specified together */ + /* -> spawn a local program instead of stdio */ + while((c->local_rfd.fd=c->local_wfd.fd=connect_local(c))>=0) { + run_client(c); + if(!c->opt->option.retry) + break; + sleep(1); /* FIXME: not a good idea in ucontext threading */ + } + } else +#endif + { + if(alloc_fd(c->local_rfd.fd)) + return NULL; + if(c->local_wfd.fd!=c->local_rfd.fd) + if(alloc_fd(c->local_wfd.fd)) + return NULL; + run_client(c); + } + free(c); +#ifdef DEBUG_STACK_SIZE + stack_info(0); /* display computed value */ +#endif +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + _endthread(); +#endif +#ifdef USE_UCONTEXT + s_log(LOG_DEBUG, "Context %ld closed", ready_head->id); + s_poll_wait(NULL, 0, 0); /* wait on poll() */ + s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context"); +#endif + return NULL; +} + +static void run_client(CLI *c) { + int error; + + c->remote_fd.fd=-1; + c->fd=-1; + c->ssl=NULL; + c->sock_bytes=c->ssl_bytes=0; + + error=setjmp(c->err); + if(!error) + do_client(c); + + s_log(LOG_NOTICE, + "Connection %s: %d bytes sent to SSL, %d bytes sent to socket", + error==1 ? "reset" : "closed", c->ssl_bytes, c->sock_bytes); + + /* Cleanup IDENT socket */ + if(c->fd>=0) + closesocket(c->fd); + + /* Cleanup SSL */ + if(c->ssl) { /* SSL initialized */ + SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + SSL_free(c->ssl); + ERR_remove_state(0); + } + + /* Cleanup remote socket */ + if(c->remote_fd.fd>=0) { /* Remote socket initialized */ + if(error==1 && c->remote_fd.is_socket) + reset(c->remote_fd.fd, "linger (remote)"); + closesocket(c->remote_fd.fd); + } + + /* Cleanup local socket */ + if(c->local_rfd.fd>=0) { /* Local socket initialized */ + if(c->local_rfd.fd==c->local_wfd.fd) { + if(error==1 && c->local_rfd.is_socket) + reset(c->local_rfd.fd, "linger (local)"); + closesocket(c->local_rfd.fd); + } else { /* STDIO */ + if(error==1 && c->local_rfd.is_socket) + reset(c->local_rfd.fd, "linger (local_rfd)"); + if(error==1 && c->local_wfd.is_socket) + reset(c->local_wfd.fd, "linger (local_wfd)"); + } + } +#ifdef USE_FORK + if(!c->opt->option.remote) /* 'exec' specified */ + child_status(); /* null SIGCHLD handler was used */ +#else + enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ + s_log(LOG_DEBUG, "%s finished (%d left)", c->opt->servname, + --num_clients); + leave_critical_section(CRIT_CLIENTS); +#endif +} + +static void do_client(CLI *c) { + init_local(c); + if(!c->opt->option.client && !c->opt->protocol) { + /* Server mode and no protocol negotiation needed */ + init_ssl(c); + init_remote(c); + } else { + init_remote(c); + negotiate(c); + init_ssl(c); + } + transfer(c); +} + +static void init_local(CLI *c) { + SOCKADDR_UNION addr; + socklen_t addrlen; + + addrlen=sizeof addr; + if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { + strcpy(c->accepted_address, "NOT A SOCKET"); + c->local_rfd.is_socket=0; + c->local_wfd.is_socket=0; /* TODO: It's not always true */ +#ifdef USE_WIN32 + if(get_last_socket_error()!=ENOTSOCK) { +#else + if(c->opt->option.transparent || get_last_socket_error()!=ENOTSOCK) { +#endif + sockerror("getpeerbyname"); + longjmp(c->err, 1); + } + /* Ignore ENOTSOCK error so 'local' doesn't have to be a socket */ + } else { /* success */ + /* copy addr to c->peer_addr */ + memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); + c->peer_addr.num=1; + s_ntop(c->accepted_address, &c->peer_addr.addr[0]); + c->local_rfd.is_socket=1; + c->local_wfd.is_socket=1; /* TODO: It's not always true */ + /* It's a socket: lets setup options */ + if(set_socket_options(c->local_rfd.fd, 1)<0) + longjmp(c->err, 1); +#ifdef USE_LIBWRAP + auth_libwrap(c); +#endif /* USE_LIBWRAP */ + auth_user(c); + s_log(LOG_NOTICE, "%s accepted connection from %s", + c->opt->servname, c->accepted_address); + } +} + +static void init_remote(CLI *c) { + /* create connection to host/service */ + if(c->opt->source_addr.num) + memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); +#ifndef USE_WIN32 + else if(c->opt->option.transparent) + memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); +#endif + else { + c->bind_addr.num=0; /* don't bind connecting socket */ + } + + /* setup c->remote_fd, now */ + if(c->opt->option.remote) { + c->remote_fd.fd=connect_remote(c); + } else /* NOT in remote mode */ + c->remote_fd.fd=connect_local(c); + c->remote_fd.is_socket=1; /* Always! */ +#ifndef USE_WIN32 + if(c->remote_fd.fd>=max_fds) { + s_log(LOG_ERR, "Remote file descriptor out of range (%d>=%d)", + c->remote_fd.fd, max_fds); + longjmp(c->err, 1); + } +#endif + s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); + if(set_socket_options(c->remote_fd.fd, 2)<0) + longjmp(c->err, 1); +} + +static void init_ssl(CLI *c) { + int i, err; + SSL_SESSION *old_session; + + if(!(c->ssl=SSL_new(c->opt->ctx))) { + sslerror("SSL_new"); + longjmp(c->err, 1); + } + SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ +#if SSLEAY_VERSION_NUMBER >= 0x0922 + SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, + strlen(sid_ctx)); +#endif + if(c->opt->option.client) { + if(c->opt->session) { + enter_critical_section(CRIT_SESSION); + SSL_set_session(c->ssl, c->opt->session); + leave_critical_section(CRIT_SESSION); + } + SSL_set_fd(c->ssl, c->remote_fd.fd); + SSL_set_connect_state(c->ssl); + } else { + if(c->local_rfd.fd==c->local_wfd.fd) + SSL_set_fd(c->ssl, c->local_rfd.fd); + else { + /* Does it make sence to have SSL on STDIN/STDOUT? */ + SSL_set_rfd(c->ssl, c->local_rfd.fd); + SSL_set_wfd(c->ssl, c->local_wfd.fd); + } + SSL_set_accept_state(c->ssl); + } + + /* Setup some values for transfer() function */ + if(c->opt->option.client) { + c->sock_rfd=&(c->local_rfd); + c->sock_wfd=&(c->local_wfd); + c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); + } else { + c->sock_rfd=c->sock_wfd=&(c->remote_fd); + c->ssl_rfd=&(c->local_rfd); + c->ssl_wfd=&(c->local_wfd); + } + + while(1) { + /* crude workaround for random MT-safety problems in OpenSSL */ + /* performance penalty is not huge, as it's a non-blocking code */ + enter_critical_section(CRIT_SSL); + if(c->opt->option.client) + i=SSL_connect(c->ssl); + else + i=SSL_accept(c->ssl); + leave_critical_section(CRIT_SSL); + err=SSL_get_error(c->ssl, i); + if(err==SSL_ERROR_NONE) + break; /* ok -> done */ + if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { + s_poll_init(&c->fds); + s_poll_add(&c->fds, c->ssl_rfd->fd, + err==SSL_ERROR_WANT_READ, + err==SSL_ERROR_WANT_WRITE); + switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { + case -1: + sockerror("init_ssl: s_poll_wait"); + longjmp(c->err, 1); + case 0: + s_log(LOG_INFO, "init_ssl: s_poll_wait timeout"); + longjmp(c->err, 1); + case 1: + break; /* OK */ + default: + s_log(LOG_ERR, "init_ssl: s_poll_wait unknown result"); + longjmp(c->err, 1); + } + continue; /* ok -> retry */ + } + if(err==SSL_ERROR_SYSCALL) { + switch(get_last_socket_error()) { + case EINTR: + case EAGAIN: + continue; + } + } + if(c->opt->option.client) + sslerror("SSL_connect"); + else + sslerror("SSL_accept"); + longjmp(c->err, 1); + } + if(SSL_session_reused(c->ssl)) { + s_log(LOG_INFO, "SSL %s: previous session reused", + c->opt->option.client ? "connected" : "accepted"); + } else { /* a new session was negotiated */ + if(c->opt->option.client) { + s_log(LOG_INFO, "SSL connected: new session negotiated"); + enter_critical_section(CRIT_SESSION); + old_session=c->opt->session; + c->opt->session=SSL_get1_session(c->ssl); /* store it */ + if(old_session) + SSL_SESSION_free(old_session); /* release the old one */ + leave_critical_section(CRIT_SESSION); + } else + s_log(LOG_INFO, "SSL accepted: new session negotiated"); + print_cipher(c); + } +} + +/****************************** some defines for transfer() */ +/* is socket/SSL open for read/write? */ +#define sock_rd (c->sock_rfd->rd) +#define sock_wr (c->sock_wfd->wr) +#define ssl_rd (c->ssl_rfd->rd) +#define ssl_wr (c->ssl_wfd->wr) +/* NOTE: above defines are related to the logical data stream, + * not the underlying file descriptors */ + +/* is socket/SSL ready for read/write? */ +#define sock_can_rd (s_poll_canread(&c->fds, c->sock_rfd->fd)) +#define sock_can_wr (s_poll_canwrite(&c->fds, c->sock_wfd->fd)) +#define ssl_can_rd (s_poll_canread(&c->fds, c->ssl_rfd->fd)) +#define ssl_can_wr (s_poll_canwrite(&c->fds, c->ssl_wfd->fd)) + +/****************************** transfer data */ +static void transfer(CLI *c) { + int watchdog=0; /* a counter to detect an infinite loop */ + int error; + socklen_t optlen; + int num, err, check_SSL_pending; + int SSL_shutdown_wants_read=0, SSL_shutdown_wants_write=0; + int SSL_write_wants_read=0, SSL_write_wants_write=0; + int SSL_read_wants_read=0, SSL_read_wants_write=0; + + c->sock_ptr=c->ssl_ptr=0; + sock_rd=sock_wr=ssl_rd=ssl_wr=1; + + do { /* main loop */ + /* set flag to try and read any buffered SSL data + * if we made room in the buffer by writing to the socket */ + check_SSL_pending=0; + + SSL_read_wants_read= + ssl_rd && c->ssl_ptrsock_ptr && !SSL_write_wants_read; + + /****************************** setup c->fds structure */ + s_poll_init(&c->fds); /* initialize the structure */ + if(sock_rd && c->sock_ptrfds, c->sock_rfd->fd, 1, 0); + if(SSL_read_wants_read || + SSL_write_wants_read || + SSL_shutdown_wants_read) + s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0); + if(sock_wr && c->ssl_ptr) + s_poll_add(&c->fds, c->sock_wfd->fd, 0, 1); + if(SSL_read_wants_write || + SSL_write_wants_write || + SSL_shutdown_wants_write) + s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1); + + /****************************** wait for an event */ + err=s_poll_wait(&c->fds, (sock_rd && ssl_rd) /* both peers open */ || + c->ssl_ptr /* data buffered to write to socket */ || + c->sock_ptr /* data buffered to write to SSL */ ? + c->opt->timeout_idle : c->opt->timeout_close, 0); + switch(err) { + case -1: + sockerror("transfer: s_poll_wait"); + longjmp(c->err, 1); + case 0: /* timeout */ + if((sock_rd && ssl_rd) || c->ssl_ptr || c->sock_ptr) { + s_log(LOG_INFO, "s_poll_wait timeout: connection reset"); + longjmp(c->err, 1); + } else { /* already closing connection */ + s_log(LOG_INFO, "s_poll_wait timeout: connection close"); + return; /* OK */ + } + } + if(!(sock_can_rd || sock_can_wr || ssl_can_rd || ssl_can_wr)) { + s_log(LOG_ERR, "INTERNAL ERROR: " + "s_poll_wait returned %d, but no descriptor is ready", err); + longjmp(c->err, 1); + } + if(!sock_rd && sock_can_rd) { + optlen=sizeof error; + if(getsockopt(c->sock_rfd->fd, SOL_SOCKET, SO_ERROR, + (void *)&error, &optlen)) + error=get_last_socket_error(); /* failed -> ask why */ + if(error) { /* really an error? */ + s_log(LOG_ERR, "Closed socket ready to read: %s (%d)", + my_strerror(error), error); + longjmp(c->err, 1); + } + if(c->ssl_ptr) { /* anything left to write */ + s_log(LOG_ERR, "Closed socket ready to read - reset"); + longjmp(c->err, 1); + } + s_log(LOG_INFO, "Closed socket ready to read - write close"); + sock_wr=0; /* no further write allowed */ + shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ + } + + /****************************** send SSL close_notify message */ + if(SSL_shutdown_wants_read || SSL_shutdown_wants_write) { + SSL_shutdown_wants_read=SSL_shutdown_wants_write=0; + num=SSL_shutdown(c->ssl); /* send close_notify */ + if(num<0) /* -1 - not completed */ + err=SSL_get_error(c->ssl, num); + else /* 0 or 1 - success */ + err=SSL_ERROR_NONE; + switch(err) { + case SSL_ERROR_NONE: /* the shutdown was successfully completed */ + s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify"); + break; + case SSL_ERROR_WANT_WRITE: + s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying"); + SSL_shutdown_wants_write=1; + break; + case SSL_ERROR_WANT_READ: + s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying"); + SSL_shutdown_wants_read=1; + break; + case SSL_ERROR_SYSCALL: /* socket error */ + parse_socket_error(c, "SSL_shutdown"); + break; + case SSL_ERROR_SSL: /* SSL error */ + sslerror("SSL_shutdown"); + longjmp(c->err, 1); + default: + s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err); + longjmp(c->err, 1); + } + } + + /****************************** write to socket */ + if(sock_wr && sock_can_wr) { + num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); + switch(num) { + case -1: /* error */ + parse_socket_error(c, "writesocket"); + break; + case 0: + s_log(LOG_DEBUG, "No data written to the socket: retrying"); + break; + default: + memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); + if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ + check_SSL_pending=1; /* check for data buffered by SSL */ + c->ssl_ptr-=num; + c->sock_bytes+=num; + watchdog=0; /* reset watchdog */ + } + } + + /****************************** write to SSL */ + if((SSL_write_wants_read && ssl_can_rd) || + (SSL_write_wants_write && ssl_can_wr)) { + SSL_write_wants_read=0; + num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); + switch(err=SSL_get_error(c->ssl, num)) { + case SSL_ERROR_NONE: + memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); + c->sock_ptr-=num; + c->ssl_bytes+=num; + watchdog=0; /* reset watchdog */ + break; + case SSL_ERROR_WANT_WRITE: /* nothing unexpected */ + break; + case SSL_ERROR_WANT_READ: + s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); + SSL_write_wants_read=1; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + s_log(LOG_DEBUG, + "SSL_write returned WANT_X509_LOOKUP: retrying"); + break; + case SSL_ERROR_SYSCALL: /* socket error */ + if(!num) { /* EOF */ + if(c->sock_ptr) { + s_log(LOG_ERR, + "SSL socket closed on SSL_write " + "with %d byte(s) in buffer", + c->sock_ptr); + longjmp(c->err, 1); /* reset the socket */ + } + s_log(LOG_DEBUG, "SSL socket closed on SSL_write"); + ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ + } else + parse_socket_error(c, "SSL_write"); + break; + case SSL_ERROR_ZERO_RETURN: /* close_notify received */ + s_log(LOG_DEBUG, "SSL closed on SSL_write"); + ssl_rd=0; + break; + case SSL_ERROR_SSL: + sslerror("SSL_write"); + longjmp(c->err, 1); + default: + s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); + longjmp(c->err, 1); + } + } + + /****************************** read from socket */ + if(sock_rd && sock_can_rd) { + num=readsocket(c->sock_rfd->fd, + c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); + switch(num) { + case -1: + parse_socket_error(c, "readsocket"); + break; + case 0: /* close */ + s_log(LOG_DEBUG, "Socket closed on read"); + sock_rd=0; + break; + default: + c->sock_ptr+=num; + watchdog=0; /* reset watchdog */ + } + } + + /****************************** read from SSL */ + if((SSL_read_wants_read && ssl_can_rd) || + (SSL_read_wants_write && ssl_can_wr) || + (check_SSL_pending && SSL_pending(c->ssl))) { + SSL_read_wants_write=0; + num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); + switch(err=SSL_get_error(c->ssl, num)) { + case SSL_ERROR_NONE: + c->ssl_ptr+=num; + watchdog=0; /* reset watchdog */ + break; + case SSL_ERROR_WANT_WRITE: + s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); + SSL_read_wants_write=1; + break; + case SSL_ERROR_WANT_READ: /* nothing unexpected */ + break; + case SSL_ERROR_WANT_X509_LOOKUP: + s_log(LOG_DEBUG, + "SSL_read returned WANT_X509_LOOKUP: retrying"); + break; + case SSL_ERROR_SYSCALL: + if(!num) { /* EOF */ + if(c->sock_ptr) { + s_log(LOG_ERR, + "SSL socket closed on SSL_read " + "with %d byte(s) in buffer", + c->sock_ptr); + longjmp(c->err, 1); /* reset the socket */ + } + s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); + ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ + } else + parse_socket_error(c, "SSL_read"); + break; + case SSL_ERROR_ZERO_RETURN: /* close_notify received */ + s_log(LOG_DEBUG, "SSL closed on SSL_read"); + ssl_rd=0; + break; + case SSL_ERROR_SSL: + sslerror("SSL_read"); + longjmp(c->err, 1); + default: + s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); + longjmp(c->err, 1); + } + } + + /****************************** check write shutdown conditions */ + if(sock_wr && !ssl_rd && !c->ssl_ptr) { + s_log(LOG_DEBUG, "Socket write shutdown"); + sock_wr=0; /* no further write allowed */ + shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ + } + if(ssl_wr && !sock_rd && !c->sock_ptr) { + s_log(LOG_DEBUG, "SSL write shutdown"); + ssl_wr=0; /* no further write allowed */ + if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */ + SSL_shutdown_wants_write=1; /* initiate close_notify */ + } else { /* no alerts in SSLv2 including close_notify alert */ + shutdown(c->sock_rfd->fd, SHUT_RD); /* notify the kernel */ + shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ + SSL_set_shutdown(c->ssl, /* notify the OpenSSL library */ + SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + ssl_rd=0; /* no further read allowed */ + } + } + + /****************************** check watchdog */ + if(++watchdog>100) { /* loop executes without transferring any data */ + s_log(LOG_ERR, + "transfer() loop executes not transferring any data"); + s_log(LOG_ERR, + "please report the problem to Michal.Trojnara@mirt.net"); + s_log(LOG_ERR, "socket open: rd=%s wr=%s, ssl open: rd=%s wr=%s", + sock_rd ? "yes" : "no", sock_wr ? "yes" : "no", + ssl_rd ? "yes" : "no", ssl_wr ? "yes" : "no"); + s_log(LOG_ERR, "socket ready: rd=%s wr=%s, ssl ready: rd=%s wr=%s", + sock_can_rd ? "yes" : "no", sock_can_wr ? "yes" : "no", + ssl_can_rd ? "yes" : "no", ssl_can_wr ? "yes" : "no"); + s_log(LOG_ERR, + "wants: SSL_read rd=%s wr=%s, " + "SSL_write rd=%s wr=%s, " + "SSL_shutdown rd=%s wr=%s", + SSL_read_wants_read ? "yes" : "no", + SSL_read_wants_write ? "yes" : "no", + SSL_write_wants_read ? "yes" : "no", + SSL_write_wants_write ? "yes" : "no", + SSL_shutdown_wants_read ? "yes" : "no", + SSL_shutdown_wants_write ? "yes" : "no"); + s_log(LOG_ERR, "socket input buffer: %d byte(s), " + "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); + s_log(LOG_ERR, "check_SSL_pending=%d", check_SSL_pending); + longjmp(c->err, 1); + } + + } while(sock_wr || ssl_wr || + SSL_shutdown_wants_read || SSL_shutdown_wants_write); +} + +static void parse_socket_error(CLI *c, const char *text) { + switch(get_last_socket_error()) { + case EINTR: + s_log(LOG_DEBUG, "%s interrupted by a signal: retrying", text); + return; + case EWOULDBLOCK: + s_log(LOG_NOTICE, "%s would block: retrying", text); + sleep(1); /* Microsoft bug KB177346 */ + return; +#if EAGAIN!=EWOULDBLOCK + case EAGAIN: + s_log(LOG_DEBUG, "%s temporary lack of resources: retrying", text); + return; +#endif + default: + sockerror(text); + longjmp(c->err, 1); + } +} + +static void print_cipher(CLI *c) { /* print negotiated cipher */ +#if SSLEAY_VERSION_NUMBER <= 0x0800 + s_log(LOG_INFO, "%s opened with SSLv%d, cipher %s", + c->opt->servname, ssl->session->ssl_version, SSL_get_cipher(c->ssl)); +#else + SSL_CIPHER *cipher; + char buf[STRLEN], *i, *j; + + cipher=(SSL_CIPHER *)SSL_get_current_cipher(c->ssl); + SSL_CIPHER_description(cipher, buf, STRLEN); + i=j=buf; + do { + switch(*i) { + case ' ': + *j++=' '; + while(i[1]==' ') + ++i; + break; + case '\n': + break; + default: + *j++=*i; + } + } while(*i++); + s_log(LOG_INFO, "Negotiated ciphers: %s", buf); +#endif +} + +static void auth_user(CLI *c) { +#ifndef _WIN32_WCE + struct servent *s_ent; /* structure for getservbyname */ +#endif + SOCKADDR_UNION ident; /* IDENT socket name */ + char name[STRLEN]; + + if(!c->opt->username) + return; /* -u option not specified */ + if((c->fd= + socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0))<0) { + sockerror("socket (auth_user)"); + longjmp(c->err, 1); + } + if(alloc_fd(c->fd)) + longjmp(c->err, 1); + memcpy(&ident, &c->peer_addr.addr[0], sizeof ident); +#ifndef _WIN32_WCE + s_ent=getservbyname("auth", "tcp"); + if(s_ent) { + ident.in.sin_port=s_ent->s_port; + } else +#endif + { + s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); + ident.in.sin_port=htons(113); + } + if(connect_blocking(c, &ident, addr_len(ident))) + longjmp(c->err, 1); + s_log(LOG_DEBUG, "IDENT server connected"); + fdprintf(c, c->fd, "%u , %u", + ntohs(c->peer_addr.addr[0].in.sin_port), + ntohs(c->opt->local_addr.addr[0].in.sin_port)); + if(fdscanf(c, c->fd, "%*[^:]: USERID :%*[^:]:%s", name)!=1) { + s_log(LOG_ERR, "Incorrect data from IDENT server"); + longjmp(c->err, 1); + } + closesocket(c->fd); + c->fd=-1; /* avoid double close on cleanup */ + if(strcmp(name, c->opt->username)) { + safestring(name); + s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)", + c->accepted_address, name); + longjmp(c->err, 1); + } + s_log(LOG_INFO, "IDENT authentication passed"); +} + +static int connect_local(CLI *c) { /* spawn local process */ +#if defined (USE_WIN32) || defined (__vms) + s_log(LOG_ERR, "LOCAL MODE NOT SUPPORTED ON WIN32 and OpenVMS PLATFORM"); + longjmp(c->err, 1); + return -1; /* some C compilers require a return value */ +#else /* USE_WIN32, __vms */ + char env[3][STRLEN], name[STRLEN], *portname; + int fd[2], pid; + X509 *peer; +#ifdef HAVE_PTHREAD_SIGMASK + sigset_t newmask; +#endif + + if (c->opt->option.pty) { + char tty[STRLEN]; + + if(pty_allocate(fd, fd+1, tty, STRLEN)) + longjmp(c->err, 1); + s_log(LOG_DEBUG, "%s allocated", tty); + } else + make_sockets(c, fd); + pid=fork(); + c->pid=(unsigned long)pid; + switch(pid) { + case -1: /* error */ + closesocket(fd[0]); + closesocket(fd[1]); + ioerror("fork"); + longjmp(c->err, 1); + case 0: /* child */ + closesocket(fd[0]); + dup2(fd[1], 0); + dup2(fd[1], 1); + if(!options.option.foreground) + dup2(fd[1], 2); + closesocket(fd[1]); + safecopy(env[0], "REMOTE_HOST="); + safeconcat(env[0], c->accepted_address); + portname=strrchr(env[0], ':'); + if(portname) /* strip the port name */ + *portname='\0'; + putenv(env[0]); + if(c->opt->option.transparent) { + putenv("LD_PRELOAD=" LIBDIR "/libstunnel.so"); + /* For Tru64 _RLD_LIST is used instead */ + putenv("_RLD_LIST=" LIBDIR "/libstunnel.so:DEFAULT"); + } + if(c->ssl) { + peer=SSL_get_peer_certificate(c->ssl); + if(peer) { + safecopy(env[1], "SSL_CLIENT_DN="); + X509_NAME_oneline(X509_get_subject_name(peer), name, STRLEN); + safestring(name); + safeconcat(env[1], name); + putenv(env[1]); + safecopy(env[2], "SSL_CLIENT_I_DN="); + X509_NAME_oneline(X509_get_issuer_name(peer), name, STRLEN); + safestring(name); + safeconcat(env[2], name); + putenv(env[2]); + X509_free(peer); + } + } +#ifdef HAVE_PTHREAD_SIGMASK + sigemptyset(&newmask); + sigprocmask(SIG_SETMASK, &newmask, NULL); +#endif + execvp(c->opt->execname, c->opt->execargs); + ioerror(c->opt->execname); /* execv failed */ + _exit(1); + default: + break; + } + /* parent */ + s_log(LOG_INFO, "Local mode child started (PID=%lu)", c->pid); + closesocket(fd[1]); +#ifdef FD_CLOEXEC + fcntl(fd[0], F_SETFD, FD_CLOEXEC); +#endif + return fd[0]; +#endif /* USE_WIN32,__vms */ +} + +#ifndef USE_WIN32 + +static void make_sockets(CLI *c, int fd[2]) { /* make a pair of connected sockets */ +#ifdef INET_SOCKET_PAIR + SOCKADDR_UNION addr; + socklen_t addrlen; + int s; /* temporary socket awaiting for connection */ + + if((s=socket(AF_INET, SOCK_STREAM, 0))<0) { + sockerror("socket#1"); + longjmp(c->err, 1); + } + if((fd[1]=socket(AF_INET, SOCK_STREAM, 0))<0) { + sockerror("socket#2"); + longjmp(c->err, 1); + } + addrlen=sizeof addr; + memset(&addr, 0, addrlen); + addr.in.sin_family=AF_INET; + addr.in.sin_addr.s_addr=htonl(INADDR_LOOPBACK); + addr.in.sin_port=htons(0); /* dynamic port allocation */ + if(bind(s, &addr.sa, addrlen)) + log_error(LOG_DEBUG, get_last_socket_error(), "bind#1"); + if(bind(fd[1], &addr.sa, addrlen)) + log_error(LOG_DEBUG, get_last_socket_error(), "bind#2"); + if(listen(s, 5)) { + sockerror("listen"); + longjmp(c->err, 1); + } + if(getsockname(s, &addr.sa, &addrlen)) { + sockerror("getsockname"); + longjmp(c->err, 1); + } + if(connect(fd[1], &addr.sa, addrlen)) { + sockerror("connect"); + longjmp(c->err, 1); + } + if((fd[0]=accept(s, &addr.sa, &addrlen))<0) { + sockerror("accept"); + longjmp(c->err, 1); + } + closesocket(s); /* don't care about the result */ +#else + if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) { + sockerror("socketpair"); + longjmp(c->err, 1); + } +#endif +} +#endif + +static int connect_remote(CLI *c) { /* connect to remote host */ + SOCKADDR_UNION addr; + SOCKADDR_LIST resolved_list, *address_list; + int fd, ind_try, ind_cur; + + /* setup address_list */ + if(c->opt->option.delayed_lookup) { + resolved_list.num=0; + if(!name2addrlist(&resolved_list, + c->opt->remote_address, DEFAULT_LOOPBACK)) { + s_log(LOG_ERR, "No host resolved"); + longjmp(c->err, 1); + } + address_list=&resolved_list; + } else /* use pre-resolved addresses */ + address_list=&c->opt->remote_addr; + + /* try to connect each host from the list */ + for(ind_try=0; ind_trynum; ind_try++) { + if(c->opt->failover==FAILOVER_RR) { + ind_cur=address_list->cur; + /* the race condition here can be safely ignored */ + address_list->cur=(ind_cur+1)%address_list->num; + } else { /* FAILOVER_PRIO */ + ind_cur=ind_try; /* ignore address_list->cur */ + } + memcpy(&addr, address_list->addr+ind_cur, sizeof addr); + + if((c->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { + sockerror("remote socket"); + longjmp(c->err, 1); + } + if(alloc_fd(c->fd)) + longjmp(c->err, 1); + + if(c->bind_addr.num) /* explicit local bind or transparent proxy */ + local_bind(c); + + if(connect_blocking(c, &addr, addr_len(addr))) { + closesocket(c->fd); + c->fd=-1; + continue; /* next IP */ + } + print_bound_address(c); + fd=c->fd; + c->fd=-1; + return fd; /* success! */ + } + longjmp(c->err, 1); + return -1; /* some C compilers require a return value */ +} + +static void local_bind(CLI *c) { + SOCKADDR_UNION addr; + +#ifdef IP_TRANSPARENT + int on=1; + if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on)) + sockerror("setsockopt IP_TRANSPARENT"); + /* ignore the error to retain Linux 2.2 compatibility */ + /* the error will be handled by bind(), anyway */ +#endif /* IP_TRANSPARENT */ + + memcpy(&addr, &c->bind_addr.addr[0], sizeof addr); + if(ntohs(addr.in.sin_port)>=1024) { /* security check */ + if(!bind(c->fd, &addr.sa, addr_len(addr))) { + s_log(LOG_INFO, "local_bind succeeded on the original port"); + return; /* success */ + } + if(get_last_socket_error()!=EADDRINUSE +#ifndef USE_WIN32 + || !c->opt->option.transparent +#endif /* USE_WIN32 */ + ) { + sockerror("local_bind (original port)"); + longjmp(c->err, 1); + } + } + + addr.in.sin_port=htons(0); /* retry with ephemeral port */ + if(!bind(c->fd, &addr.sa, addr_len(addr))) { + s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); + return; /* success */ + } + sockerror("local_bind (ephemeral port)"); + longjmp(c->err, 1); +} + +static void print_bound_address(CLI *c) { + char txt[IPLEN]; + SOCKADDR_UNION addr; + socklen_t addrlen=sizeof addr; + + memset(&addr, 0, addrlen); + if(getsockname(c->fd, (struct sockaddr *)&addr, &addrlen)) { + sockerror("getsockname"); + } else { + s_ntop(txt, &addr); + s_log(LOG_NOTICE,"%s connected remote server from %s", + c->opt->servname, txt); + } +} + +static void reset(int fd, char *txt) { + /* Set lingering on a socket if needed*/ + struct linger l; + + l.l_onoff=1; + l.l_linger=0; + if(setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof l)) + log_error(LOG_DEBUG, get_last_socket_error(), txt); +} + +/* End of client.c */ --- a/src/common.h +++ b/src/common.h @@ -53,6 +53,9 @@ /* I/O buffer size */ #define BUFFSIZE 16384 +/* maximum space reserved for header insertion in BUFFSIZE */ +#define BUFF_RESERVED 1024 + /* Length of strings (including the terminating '\0' character) */ /* It can't be lower than 256 bytes or NTLM authentication will break */ #define STRLEN 256 --- /dev/null +++ b/src/common.h.orig @@ -0,0 +1,429 @@ +/* + * stunnel Universal SSL tunnel + * Copyright (C) 1998-2009 Michal Trojnara + * + * 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 . + * + * 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. + */ + +#ifndef COMMON_H +#define COMMON_H + +#ifndef VERSION +#define VERSION "unknown" +#endif + +/**************************************** Common constants */ + +#define LIBWRAP_CLIENTS 5 + +/* CPU stack size */ +#define DEFAULT_STACK_SIZE 65536 +/* #define DEBUG_STACK_SIZE */ + +/* I/O buffer size */ +#define BUFFSIZE 16384 + +/* Length of strings (including the terminating '\0' character) */ +/* It can't be lower than 256 bytes or NTLM authentication will break */ +#define STRLEN 256 + +/* IP address and TCP port textual representation length */ +#define IPLEN 128 + +/* How many bytes of random input to read from files for PRNG */ +/* OpenSSL likes at least 128 bits, so 64 bytes seems plenty. */ +#define RANDOM_BYTES 64 + +/* For FormatGuard */ +/* #define __NO_FORMATGUARD_ */ + +/**************************************** Platform */ + +#ifdef USE_WIN32 +#define USE_IPv6 +/* #define USE_FIPS */ +#endif + +#ifdef _WIN32_WCE +#define USE_WIN32 +typedef int socklen_t; +#define EINTR WSAEINTR +#define EMFILE WSAEMFILE +#endif + +#ifdef USE_WIN32 +#define HAVE_OPENSSL +#define HAVE_OSSL_ENGINE_H +/* prevent including wincrypt.h, as it defines it's own OCSP_RESPONSE */ +#define __WINCRYPT_H__ +#endif + +/**************************************** Generic headers */ + +#ifdef __vms +#include +#endif /* __vms */ + +/* For nsr-tandem-nsk architecture */ +#ifdef __TANDEM +#include +#endif + +/* threads model */ +#ifdef USE_UCONTEXT +#define __MAKECONTEXT_V2_SOURCE +#include +#endif + +#ifdef USE_PTHREAD +#define THREADS +#define _REENTRANT +#define _THREAD_SAFE +#include +#endif + +/* TCP wrapper */ +#if HAVE_TCPD_H && HAVE_LIBWRAP +#define USE_LIBWRAP +#endif + +/* Must be included before sys/stat.h for Ultrix */ +#include /* u_short, u_long */ +/* General headers */ +#include +/* Must be included before sys/stat.h for Ultrix */ +#ifndef _WIN32_WCE +#include +#endif +#include +#include /* va_ */ +#include +#include /* isalnum */ +#include +#include /* stat */ +#include + +/**************************************** WIN32 headers */ + +#ifdef USE_WIN32 + +#ifndef HOST +#ifdef __MINGW32__ +#define HOST "x86-pc-mingw32-gnu" +#else +#ifdef _MSC_VER +#define _QUOTEME(x) #x +#define QUOTEME(x) _QUOTEME(x) +#define HOST "x86-pc-msvc-" ## QUOTEME(_MSC_VER) +#else +#define HOST "x86-pc-unknown" +#endif +#endif +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +#define HAVE_SNPRINTF +#define snprintf _snprintf +#define HAVE_VSNPRINTF +#define vsnprintf _vsnprintf +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define sleep(c) Sleep(1000*(c)) + +#define get_last_socket_error() WSAGetLastError() +#define get_last_error() GetLastError() +#define readsocket(s,b,n) recv((s),(b),(n),0) +#define writesocket(s,b,n) send((s),(b),(n),0) + +/* #define FD_SETSIZE 4096 */ +/* #define Win32_Winsock */ +#define __USE_W32_SOCKETS + +/* Winsock2 header for IPv6 definitions */ +#ifdef _WIN32_WCE +#include +#else +#include +#include +#endif +#include + +#define ECONNRESET WSAECONNRESET +#define ENOTSOCK WSAENOTSOCK +#define ENOPROTOOPT WSAENOPROTOOPT +#define EINPROGRESS WSAEINPROGRESS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EISCONN WSAEISCONN +#define EADDRINUSE WSAEADDRINUSE + +#ifdef EINVAL +#undef EINVAL +#endif +#define EINVAL WSAEINVAL + +#include /* _beginthread */ +#include + +#define NO_IDEA +#define OPENSSL_NO_IDEA + +/**************************************** non-WIN32 headers */ + +#else /* USE_WIN32 */ + +#if SIZEOF_UNSIGNED_CHAR == 1 +typedef unsigned char u8; +#endif + +#if SIZEOF_UNSIGNED_SHORT == 2 +typedef unsigned short u16; +#else +typedef unsigned int u16; +#endif + +#if SIZEOF_UNSIGNED_INT == 4 +typedef unsigned int u32; +#else +typedef unsigned long u32; +#endif + +#ifdef __INNOTEK_LIBC__ +# define get_last_socket_error() sock_errno() +# define get_last_error() errno +# define readsocket(s,b,n) recv((s),(b),(n),0) +# define writesocket(s,b,n) send((s),(b),(n),0) +# define closesocket(s) close(s) +# define ioctlsocket(a,b,c) so_ioctl((a),(b),(c)) +#else +#define get_last_socket_error() errno +#define get_last_error() errno +#define readsocket(s,b,n) read((s),(b),(n)) +#define writesocket(s,b,n) write((s),(b),(n)) +#define closesocket(s) close(s) +#define ioctlsocket(a,b,c) ioctl((a),(b),(c)) +#endif + /* OpenVMS compatibility */ +#ifdef __vms +#define libdir "__NA__" +#define PIDFILE "SYS$LOGIN:STUNNEL.PID" +#ifdef __alpha +#define HOST "alpha-openvms" +#else +#define HOST "vax-openvms" +#endif +#include +#include +#else /* __vms */ +#include +#endif /* __vms */ + + /* Unix-specific headers */ +#include /* signal */ +#include /* wait */ +#ifdef HAVE_SYS_RESOURCE_H +#include /* getrlimit */ +#endif +#ifdef HAVE_UNISTD_H +#include /* getpid, fork, execvp, exit */ +#endif +#ifdef HAVE_STROPTS_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include /* for aix */ +#endif + +#ifndef BROKEN_POLL +#ifdef HAVE_POLL_H +#include +#define USE_POLL +#else /* HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include +#define USE_POLL +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ +#endif /* BROKEN_POLL */ + +#ifdef HAVE_SYS_FILIO_H +#include /* for FIONBIO */ +#endif +#include +#ifdef HAVE_GRP_H +#include +#endif +#ifdef __BEOS__ +#include +#endif +#include + +#include /* struct sockaddr_in */ +#include /* getpeername */ +#include /* inet_ntoa */ +#include /* select */ +#include /* ioctl */ +#include +#include +#ifndef INADDR_ANY +#define INADDR_ANY (u32)0x00000000 +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (u32)0x7F000001 +#endif + +#if defined(HAVE_WAITPID) +/* For SYSV systems */ +#define wait_for_pid(a, b, c) waitpid((a), (b), (c)) +#define HAVE_WAIT_FOR_PID 1 +#elif defined(HAVE_WAIT4) +/* For BSD systems */ +#define wait_for_pid(a, b, c) wait4((a), (b), (c), NULL) +#define HAVE_WAIT_FOR_PID 1 +#endif + +/* SunOS 4 */ +#if defined(sun) && !defined(__svr4__) && !defined(__SVR4) +#define atexit(a) on_exit((a), NULL) +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(num) ((num)==0 ? "No error" : \ + ((num)>=sys_nerr ? "Unknown error" : sys_errlist[num])) +#endif /* SunOS 4 */ + +/* AIX does not have SOL_TCP defined */ +#ifndef SOL_TCP +#define SOL_TCP SOL_SOCKET +#endif /* SOL_TCP */ + +/* Linux */ +#ifdef __linux__ +#ifndef IP_TRANSPARENT +/* old kernel headers without IP_TRANSPARENT definition */ +#define IP_TRANSPARENT 19 +#endif /* IP_TRANSPARENT */ +#endif /* __linux__ */ + +#endif /* USE_WIN32 */ + +/**************************************** OpenSSL headers */ + +#ifdef HAVE_OPENSSL + +#define OPENSSL_THREAD_DEFINES +#include +#if !defined(OPENSSL_THREADS) && defined(USE_PTHREAD) +#error OpenSSL library compiled without thread support +#endif /* !OPENSSL_THREADS && USE_PTHREAD */ + +#include +#include +#include +#include /* for CRYPTO_* and SSLeay_version */ +#include +#include +#include + +#ifdef HAVE_OSSL_ENGINE_H +#include +#endif /* HAVE_OSSL_ENGINE_H */ + +#if SSLEAY_VERSION_NUMBER >= 0x00907000L +#include +#endif /* OpenSSL-0.9.7 */ + +#ifdef USE_FIPS +#include +#include +#endif /* USE_FIPS */ + +#else /* HAVE_OPENSSL */ + +#include +#include +#include +#include +#include +#include + +#endif /* HAVE_OPENSSL */ + +/**************************************** Other defines */ + +/* Safe copy for strings declarated as char[STRLEN] */ +#define safecopy(dst, src) \ + (dst[STRLEN-1]='\0', strncpy((dst), (src), STRLEN-1)) +#define safeconcat(dst, src) \ + (dst[STRLEN-1]='\0', strncat((dst), (src), STRLEN-strlen(dst)-1)) +/* change all non-printable characters to '.' */ +#define safestring(s) \ + do {unsigned char *p; for(p=(unsigned char *)(s); *p; p++) \ + if(!isprint((int)*p)) *p='.';} while(0) +/* change all unsafe characters to '.' */ +#define safename(s) \ + do {unsigned char *p; for(p=(s); *p; p++) \ + if(!isalnum((int)*p)) *p='.';} while(0) + +/* Some definitions for IPv6 support */ +#if defined(USE_IPv6) +#define addr_len(x) ((x).sa.sa_family==AF_INET ? \ + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +#else +#define addr_len(x) (sizeof(struct sockaddr_in)) +#endif + +/* Always use IPv4 defaults! */ +#define DEFAULT_LOOPBACK "127.0.0.1" +#define DEFAULT_ANY "0.0.0.0" +#if 0 +#define DEFAULT_LOOPBACK "::1" +#define DEFAULT_ANY "::" +#endif + +#if defined (USE_WIN32) || defined (__vms) +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 +#endif /* defined (USE_WIN32) || defined (__vms) */ +#define LOG_RAW -1 + +#endif /* defined COMMON_H */ + +/* End of common.h */ --- a/src/options.c +++ b/src/options.c @@ -781,6 +781,28 @@ static char *service_options(CMD cmd, LO } #endif + /* xforwardedfor */ + switch(cmd) { + case CMD_INIT: + section->option.xforwardedfor=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "xforwardedfor")) + break; + if(!strcasecmp(arg, "yes")) + section->option.xforwardedfor=1; + else if(!strcasecmp(arg, "no")) + section->option.xforwardedfor=0; + else + return "argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log("%-15s = yes|no append an HTTP X-Forwarded-For header","xforwardedfor"); + break; + } + /* exec */ #ifndef USE_WIN32 switch(cmd) { --- /dev/null +++ b/src/options.c.orig @@ -0,0 +1,1994 @@ +/* + * stunnel Universal SSL tunnel + * Copyright (C) 1998-2009 Michal Trojnara + * + * 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 . + * + * 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 + +#ifdef USE_WIN32 +#define CONFSEPARATOR "\\" +#else +#define CONFSEPARATOR "/" +#endif + +#define CONFLINELEN (16*1024) + +static void section_validate(char *, int, LOCAL_OPTIONS *, int); +static void config_error(char *, int, char *); +static char *stralloc(char *); +#ifndef USE_WIN32 +static char **argalloc(char *); +#endif + +static int parse_debug_level(char *); +static int parse_ssl_option(char *); +static int print_socket_options(void); +static void print_option(char *, int, OPT_UNION *); +static int parse_socket_option(char *); +static char *parse_ocsp_url(LOCAL_OPTIONS *, char *); +static unsigned long parse_ocsp_flag(char *); + +GLOBAL_OPTIONS options; +LOCAL_OPTIONS local_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 *global_options(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_RAW, "Global options"); + } + + /* chroot */ +#ifdef HAVE_CHROOT + switch(cmd) { + case CMD_INIT: + options.chroot_dir=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "chroot")) + break; + options.chroot_dir=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = directory to chroot stunnel process", "chroot"); + break; + } +#endif /* HAVE_CHROOT */ + + /* compression */ + switch(cmd) { + case CMD_INIT: + options.compression=COMP_NONE; + break; + case CMD_EXEC: + if(strcasecmp(opt, "compression")) + break; + if(!strcasecmp(arg, "zlib")) + options.compression=COMP_ZLIB; + else if(!strcasecmp(arg, "rle")) + options.compression=COMP_RLE; + else + return "Compression type should be either 'zlib' or 'rle'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = zlib|rle compression type", + "compression"); + break; + } + + /* debug */ + switch(cmd) { + case CMD_INIT: + options.debug_level=5; +#if !defined (USE_WIN32) && !defined (__vms) + 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: + s_log(LOG_RAW, "%-15s = %d", "debug", options.debug_level); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = [facility].level (e.g. daemon.info)", "debug"); + break; + } + + /* EGD is only supported when compiled with OpenSSL 0.9.5a or later */ +#if SSLEAY_VERSION_NUMBER >= 0x0090581fL + switch(cmd) { + case CMD_INIT: + options.egd_sock=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "EGD")) + break; + options.egd_sock=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: +#ifdef EGD_SOCKET + s_log(LOG_RAW, "%-15s = %s", "EGD", EGD_SOCKET); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = path to Entropy Gathering Daemon socket", "EGD"); + break; + } +#endif /* OpenSSL 0.9.5a */ + +#ifdef HAVE_OSSL_ENGINE_H + /* engine */ + switch(cmd) { + case CMD_INIT: + break; + case CMD_EXEC: + if(strcasecmp(opt, "engine")) + break; + open_engine(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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'; + ctrl_engine(arg, tmpstr); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = cmd[:arg]", + "engineCtrl"); + break; + } +#endif + + /* fips */ +#ifdef USE_FIPS + switch(cmd) { + case CMD_INIT: + options.option.fips=1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "fips")) + break; + if(!strcasecmp(arg, "yes")) + options.option.fips=1; + else if(!strcasecmp(arg, "no")) + 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_RAW, "%-15s = yes|no FIPS 140-2 mode", + "fips"); + break; + } +#endif /* USE_FIPS */ + + /* foreground */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + options.option.foreground=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "foreground")) + break; + if(!strcasecmp(arg, "yes")) + options.option.foreground=1; + else if(!strcasecmp(arg, "no")) + 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_RAW, "%-15s = yes|no foreground mode (don't fork, log to stderr)", + "foreground"); + break; + } +#endif + + /* output */ + switch(cmd) { + case CMD_INIT: + options.output_file=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "output")) + break; + options.output_file=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = file to append log messages", "output"); + break; + } + + /* pid */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + options.pidfile=PIDFILE; + break; + case CMD_EXEC: + if(strcasecmp(opt, "pid")) + break; + if(arg[0]) /* is argument not empty? */ + options.pidfile=stralloc(arg); + else + options.pidfile=NULL; /* empty -> do not create a pid file */ + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %s", "pid", PIDFILE); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = pid file (empty to disable creating)", "pid"); + break; + } +#endif + + /* RNDbytes */ + switch(cmd) { + case CMD_INIT: + options.random_bytes=RANDOM_BYTES; + break; + case CMD_EXEC: + if(strcasecmp(opt, "RNDbytes")) + break; + options.random_bytes=atoi(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d", "RNDbytes", RANDOM_BYTES); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = bytes to read from random seed files", "RNDbytes"); + break; + } + + /* RNDfile */ + switch(cmd) { + case CMD_INIT: + options.rand_file=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "RNDfile")) + break; + options.rand_file=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: +#ifdef RANDOM_FILE + s_log(LOG_RAW, "%-15s = %s", "RNDfile", RANDOM_FILE); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = path to file with random seed data", "RNDfile"); + break; + } + + /* RNDoverwrite */ + switch(cmd) { + case CMD_INIT: + options.option.rand_write=1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "RNDoverwrite")) + break; + if(!strcasecmp(arg, "yes")) + options.option.rand_write=1; + else if(!strcasecmp(arg, "no")) + options.option.rand_write=0; + else + return "Argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = yes", "RNDoverwrite"); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = yes|no overwrite seed datafiles with new random data", + "RNDoverwrite"); + break; + } + + /* service */ + switch(cmd) { + case CMD_INIT: + local_options.servname=stralloc("stunnel"); +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + options.win32_service="stunnel"; +#endif + break; + case CMD_EXEC: + if(strcasecmp(opt, "service")) + break; + local_options.servname=stralloc(arg); +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + options.win32_service=stralloc(arg); +#endif + return NULL; /* OK */ + case CMD_DEFAULT: +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + s_log(LOG_RAW, "%-15s = %s", "service", options.win32_service); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = service name", "service"); + break; + } + +#ifndef USE_WIN32 + /* setgid */ + switch(cmd) { + case CMD_INIT: + options.gid=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "setgid")) + break; + gr=getgrnam(arg); + if(gr) + options.gid=gr->gr_gid; + else if(atoi(arg)) /* numerical? */ + options.gid=atoi(arg); + else + return "Illegal GID"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = groupname for setgid()", "setgid"); + break; + } +#endif + +#ifndef USE_WIN32 + /* setuid */ + switch(cmd) { + case CMD_INIT: + options.uid=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "setuid")) + break; + pw=getpwnam(arg); + if(pw) + options.uid=pw->pw_uid; + else if(atoi(arg)) /* numerical? */ + options.uid=atoi(arg); + else + return "Illegal UID"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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_RAW, "%-15s = a|l|r:option=value[:value]", "socket"); + s_log(LOG_RAW, "%18sset an option on accept/local/remote socket", ""); + break; + } + + /* syslog */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + options.option.syslog=1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "syslog")) + break; + if(!strcasecmp(arg, "yes")) + options.option.syslog=1; + else if(!strcasecmp(arg, "no")) + 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_RAW, "%-15s = yes|no send logging messages to syslog", + "syslog"); + break; + } +#endif + + /* taskbar */ +#ifdef USE_WIN32 + switch(cmd) { + case CMD_INIT: + options.option.taskbar=1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "taskbar")) + break; + if(!strcasecmp(arg, "yes")) + options.option.taskbar=1; + else if(!strcasecmp(arg, "no")) + options.option.taskbar=0; + else + return "Argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = yes", "taskbar"); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = yes|no enable the taskbar icon", "taskbar"); + break; + } +#endif + + if(cmd==CMD_EXEC) + return option_not_found; + return NULL; /* OK */ +} + +static char *service_options(CMD cmd, LOCAL_OPTIONS *section, + char *opt, char *arg) { + int tmpnum; + + if(cmd==CMD_DEFAULT || cmd==CMD_HELP) { + s_log(LOG_RAW, " "); + s_log(LOG_RAW, "Service-level options"); + } + + /* accept */ + switch(cmd) { + case CMD_INIT: + section->option.accept=0; + memset(§ion->local_addr, 0, sizeof(SOCKADDR_LIST)); + section->local_addr.addr[0].in.sin_family=AF_INET; + break; + case CMD_EXEC: + if(strcasecmp(opt, "accept")) + break; + section->option.accept=1; + if(!name2addrlist(§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_RAW, "%-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=stralloc(arg); + else + section->ca_dir=NULL; + return NULL; /* OK */ + case CMD_DEFAULT: +#if 0 + s_log(LOG_RAW, "%-15s = %s", "CApath", + section->ca_dir ? section->ca_dir : "(none)"); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + else + section->ca_file=NULL; + return NULL; /* OK */ + case CMD_DEFAULT: +#if 0 + s_log(LOG_RAW, "%-15s = %s", "CAfile", + section->ca_file ? section->ca_file : "(none)"); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = CA certificate file for 'verify' option", + "CAfile"); + break; + } + + /* cert */ + switch(cmd) { + case CMD_INIT: +#ifdef CONFDIR + section->cert=CONFDIR CONFSEPARATOR "stunnel.pem"; +#else + section->cert="stunnel.pem"; +#endif + break; + case CMD_EXEC: + if(strcasecmp(opt, "cert")) + break; + section->cert=stralloc(arg); + section->option.cert=1; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %s", "cert", section->cert); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = certificate chain", "cert"); + break; + } + + /* ciphers */ +#ifdef USE_FIPS +#define STUNNEL_DEFAULT_CIPHER_LIST "FIPS" +#else +#define STUNNEL_DEFAULT_CIPHER_LIST SSL_DEFAULT_CIPHER_LIST +#endif /* USE_FIPS */ + switch(cmd) { + case CMD_INIT: + section->cipher_list=STUNNEL_DEFAULT_CIPHER_LIST; + break; + case CMD_EXEC: + if(strcasecmp(opt, "ciphers")) + break; + section->cipher_list=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %s", "ciphers", STUNNEL_DEFAULT_CIPHER_LIST); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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_RAW, "%-15s = yes|no client mode (remote service uses SSL)", + "client"); + break; + } + + /* connect */ + switch(cmd) { + case CMD_INIT: + section->option.remote=0; + section->remote_address=NULL; + section->remote_addr.num=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "connect")) + break; + section->option.remote=1; + section->remote_address=stralloc(arg); + if(!section->option.delayed_lookup && + !name2addrlist(§ion->remote_addr, arg, DEFAULT_LOOPBACK)) { + s_log(LOG_RAW, "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_RAW, "%-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=stralloc(arg); + else + section->crl_dir=NULL; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + else + section->crl_file=NULL; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = CRL file", "CRLfile"); + break; + } + + /* 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_RAW, "%-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; + section->engine=get_engine(atoi(arg)); + if(!section->engine) + return "Illegal engine number"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = number of engine to read the key from", + "engineNum"); + break; + } +#endif + + /* exec */ +#ifndef USE_WIN32 + 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=stralloc(arg); + if(!section->execargs) { + section->execargs=calloc(2, sizeof(char *)); + section->execargs[0]=section->execname; + section->execargs[1]=NULL; /* to show that it's null-terminated */ + } + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = file execute local inetd-type program", + "exec"); + break; + } +#endif + + /* execargs */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + section->execargs=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "execargs")) + break; + section->execargs=argalloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = arguments for 'exec' (including $0)", + "execargs"); + break; + } +#endif + + /* 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_RAW, "%-15s = rr|prio chose failover strategy", + "failover"); + break; + } + + /* ident */ + switch(cmd) { + case CMD_INIT: + section->username=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "ident")) + break; + section->username=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %s", "key", section->cert); /* set in stunnel.c */ + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = certificate private key", "key"); + break; + } + + /* local */ + switch(cmd) { + case CMD_INIT: + memset(§ion->source_addr, 0, sizeof(SOCKADDR_LIST)); + section->source_addr.addr[0].in.sin_family=AF_INET; + break; + case CMD_EXEC: + if(strcasecmp(opt, "local")) + break; + if(!hostport2addrlist(§ion->source_addr, arg, "0")) + return "Failed to resolve local address"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = IP address to be used as source for remote" + " connections", "local"); + break; + } + +#if SSLEAY_VERSION_NUMBER >= 0x00907000L + /* OCSP */ + switch(cmd) { + case CMD_INIT: + section->option.ocsp=0; + memset(§ion->ocsp_addr, 0, sizeof(SOCKADDR_LIST)); + section->ocsp_addr.addr[0].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_RAW, "%-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_RAW, "%-15s = OCSP server flags", "OCSPflag"); + break; + } +#endif /* OpenSSL-0.9.7 */ + + /* 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_RAW, "%-15s = SSL option", "options"); + s_log(LOG_RAW, "%18sset an SSL option", ""); + break; + } + + /* protocol */ + switch(cmd) { + case CMD_INIT: + section->protocol=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "protocol")) + break; + section->protocol=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = protocol to negotiate before SSL initialization", + "protocol"); + s_log(LOG_RAW, "%18scurrently supported: cifs, connect, nntp, pop3, smtp", ""); + break; + } + + /* protocolAuthentication */ + switch(cmd) { + case CMD_INIT: + section->protocol_authentication="basic"; + break; + case CMD_EXEC: + if(strcasecmp(opt, "protocolAuthentication")) + break; + section->protocol_authentication=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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_RAW, "%-15s = yes|no allocate pseudo terminal for 'exec' option", + "pty"); + break; + } +#endif + + /* retry */ +#ifndef USE_WIN32 + 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_RAW, "%-15s = yes|no retry connect+exec section", + "retry"); + break; + } +#endif + + /* session */ + switch(cmd) { + case CMD_INIT: + section->session_timeout=300; + break; + case CMD_EXEC: + if(strcasecmp(opt, "session")) + break; + if(atoi(arg)>0) + section->session_timeout=atoi(arg); + else + return "Illegal session timeout"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %ld seconds", "session", section->session_timeout); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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_LIST)); + section->sessiond_addr.addr[0].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(!name2addrlist(§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_RAW, "%-15s = [host:]port use sessiond at host:port", + "sessiond"); + 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; + if(atoi(arg)>0) + section->stack_size=atoi(arg); + else + return "Illegal thread stack size"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d bytes", "stack", section->stack_size); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = thread stack size (in bytes)", "stack"); + break; + } +#endif + + /* sslVersion */ + switch(cmd) { + case CMD_INIT: +#ifdef USE_FIPS + section->client_method=(SSL_METHOD *)TLSv1_client_method(); + section->server_method=(SSL_METHOD *)TLSv1_server_method(); +#else + section->client_method=(SSL_METHOD *)SSLv3_client_method(); + section->server_method=(SSL_METHOD *)SSLv23_server_method(); +#endif + 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")) { + section->client_method=(SSL_METHOD *)SSLv2_client_method(); + section->server_method=(SSL_METHOD *)SSLv2_server_method(); + } else if(!strcasecmp(arg, "SSLv3")) { + section->client_method=(SSL_METHOD *)SSLv3_client_method(); + section->server_method=(SSL_METHOD *)SSLv3_server_method(); + } else if(!strcasecmp(arg, "TLSv1")) { + section->client_method=(SSL_METHOD *)TLSv1_client_method(); + section->server_method=(SSL_METHOD *)TLSv1_server_method(); + } else + return "Incorrect version of SSL protocol"; + return NULL; /* OK */ + case CMD_DEFAULT: +#ifdef USE_FIPS + s_log(LOG_RAW, "%-15s = TLSv1", "sslVersion"); +#else + s_log(LOG_RAW, "%-15s = SSLv3 for client, all for server", "sslVersion"); +#endif + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = all|SSLv2|SSLv3|TLSv1 SSL method", "sslVersion"); + break; + } + + /* TIMEOUTbusy */ + switch(cmd) { + case CMD_INIT: + section->timeout_busy=300; /* 5 minutes */ + break; + case CMD_EXEC: + if(strcasecmp(opt, "TIMEOUTbusy")) + break; + if(atoi(arg)>0) + section->timeout_busy=atoi(arg); + else + return "Illegal busy timeout"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTbusy", section->timeout_busy); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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; + if(atoi(arg)>0 || !strcmp(arg, "0")) + section->timeout_close=atoi(arg); + else + return "Illegal close timeout"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTclose", section->timeout_close); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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; + if(atoi(arg)>0 || !strcmp(arg, "0")) + section->timeout_connect=atoi(arg); + else + return "Illegal connect timeout"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTconnect", + section->timeout_connect); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-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; + if(atoi(arg)>0) + section->timeout_idle=atoi(arg); + else + return "Illegal idle timeout"; + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTidle", section->timeout_idle); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = seconds to keep an idle connection", "TIMEOUTidle"); + break; + } + + /* transparent */ +#ifndef USE_WIN32 + switch(cmd) { + case CMD_INIT: + section->option.transparent=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "transparent")) + break; + if(!strcasecmp(arg, "yes")) + section->option.transparent=1; + else if(!strcasecmp(arg, "no")) + section->option.transparent=0; + else + return "Argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = yes|no transparent proxy mode", + "transparent"); + break; + } +#endif + + /* verify */ + switch(cmd) { + case CMD_INIT: + section->verify_level=-1; + section->verify_use_only_my=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "verify")) + break; + section->verify_level=SSL_VERIFY_NONE; + switch(atoi(arg)) { + case 3: + section->verify_use_only_my=1; + case 2: + section->verify_level|=SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + case 1: + section->verify_level|=SSL_VERIFY_PEER; + case 0: + return NULL; /* OK */ + default: + return "Bad verify level"; + } + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = none", "verify"); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = level of peer certificate verification", "verify"); + s_log(LOG_RAW, "%18slevel 1 - verify peer certificate if present", ""); + s_log(LOG_RAW, "%18slevel 2 - require valid peer certificate always", ""); + s_log(LOG_RAW, "%18slevel 3 - verify peer with locally installed certificate", + ""); + break; + } + + if(cmd==CMD_EXEC) + return option_not_found; + return NULL; /* OK */ +} + +static void syntax(char *confname) { + s_log(LOG_RAW, " "); + s_log(LOG_RAW, "Syntax:"); + s_log(LOG_RAW, "stunnel " +#ifdef USE_WIN32 +#ifndef _WIN32_WCE + "[ [-install | -uninstall] " +#endif + "[-quiet] " +#endif + "[] ] " +#ifndef USE_WIN32 + "-fd " +#endif + "| -help | -version | -sockets"); + s_log(LOG_RAW, " - use specified config file instead of %s", + confname); +#ifdef USE_WIN32 +#ifndef _WIN32_WCE + s_log(LOG_RAW, " -install - install NT service"); + s_log(LOG_RAW, " -uninstall - uninstall NT service"); +#endif + s_log(LOG_RAW, " -quiet - don't display a message box on success"); +#else + s_log(LOG_RAW, " -fd - read the config file from a file descriptor"); +#endif + s_log(LOG_RAW, " -help - get config file help"); + s_log(LOG_RAW, " -version - display version and defaults"); + s_log(LOG_RAW, " -sockets - display default socket options"); + die(1); +} + +void parse_config(char *name, char *parameter) { +#ifdef CONFDIR + char *default_config_file=CONFDIR CONFSEPARATOR "stunnel.conf"; +#else + char *default_config_file="stunnel.conf"; +#endif + DISK_FILE *df; + char confline[CONFLINELEN], *arg, *opt, *errstr, *filename; + int line_number, i; +#ifdef MAX_FD + int sections=0; +#endif + LOCAL_OPTIONS *section, *new_section; + + memset(&options, 0, sizeof(GLOBAL_OPTIONS)); /* reset global options */ + + memset(&local_options, 0, sizeof(LOCAL_OPTIONS)); /* reset local options */ + local_options.next=NULL; + section=&local_options; + + global_options(CMD_INIT, NULL, NULL); + service_options(CMD_INIT, section, NULL, NULL); + if(!name) + name=default_config_file; + if(!strcasecmp(name, "-help")) { + global_options(CMD_HELP, NULL, NULL); + service_options(CMD_HELP, section, NULL, NULL); + die(1); + } + if(!strcasecmp(name, "-version")) { + stunnel_info(1); + s_log(LOG_RAW, " "); + global_options(CMD_DEFAULT, NULL, NULL); + service_options(CMD_DEFAULT, section, NULL, NULL); + die(1); + } + if(!strcasecmp(name, "-sockets")) { + print_socket_options(); + die(1); + } +#ifndef USE_WIN32 + if(!strcasecmp(name, "-fd")) { + if(!parameter) { + s_log(LOG_RAW, "No file descriptor specified"); + syntax(default_config_file); + } + for(arg=parameter, i=0; *arg; ++arg) { + if(*arg<'0' || *arg>'9') { + s_log(LOG_RAW, "Invalid file descriptor %s", parameter); + syntax(default_config_file); + } + i=10*i+*arg-'0'; + } + df=file_fdopen(i); + if(!df) { + s_log(LOG_RAW, "Invalid file descriptor %s", parameter); + syntax(default_config_file); + } + filename="descriptor"; + } else +#endif + { + df=file_open(name, 0); + if(!df) + syntax(default_config_file); + filename=name; + } + line_number=0; + while(file_getline(df, confline, CONFLINELEN)) { + ++line_number; + opt=confline; + while(isspace((unsigned char)*opt)) + ++opt; /* remove initial whitespaces */ + for(i=strlen(opt)-1; i>=0 && isspace((unsigned char)opt[i]); --i) + opt[i]='\0'; /* remove trailing whitespaces */ + if(opt[0]=='\0' || opt[0]=='#' || opt[0]==';') /* empty or comment */ + continue; + if(opt[0]=='[' && opt[strlen(opt)-1]==']') { /* new section */ + section_validate(filename, line_number, section, 0); + ++opt; + opt[strlen(opt)-1]='\0'; + new_section=calloc(1, sizeof(LOCAL_OPTIONS)); + if(!new_section) { + s_log(LOG_RAW, "Fatal memory allocation error"); + die(2); + } + memcpy(new_section, &local_options, sizeof(LOCAL_OPTIONS)); + new_section->servname=stralloc(opt); + new_section->session=NULL; + new_section->next=NULL; + section->next=new_section; + section=new_section; +#ifdef MAX_FD + if(++sections>MAX_FD) + config_error(filename, line_number, "Too many sections"); +#endif + continue; + } + arg=strchr(confline, '='); + if(!arg) + config_error(filename, line_number, "No '=' found"); + *arg++='\0'; /* split into option name and argument value */ + for(i=strlen(opt)-1; i>=0 && isspace((unsigned char)opt[i]); --i) + opt[i]='\0'; /* remove trailing whitespaces */ + while(isspace((unsigned char)*arg)) + ++arg; /* remove initial whitespaces */ + errstr=service_options(CMD_EXEC, section, opt, arg); + if(section==&local_options && errstr==option_not_found) + errstr=global_options(CMD_EXEC, opt, arg); + config_error(filename, line_number, errstr); + } + section_validate(filename, line_number, section, 1); + file_close(df); + if(!local_options.next) { /* inetd mode */ + if (section->option.accept) { + s_log(LOG_RAW, "accept option is not allowed in inetd mode"); + s_log(LOG_RAW, "remove accept option or define a [section]"); + die(1); + } + if (!section->option.remote && !section->execname) { + s_log(LOG_RAW, "inetd mode must define a remote host or an executable"); + die(1); + } + } +} + +static void section_validate(char *filename, int line_number, + LOCAL_OPTIONS *section, int final) { + if(section==&local_options) { /* global options just configured */ +#ifdef HAVE_OSSL_ENGINE_H + close_engine(); +#endif + ssl_configure(); /* configure global SSL settings */ + if(!final) /* no need to validate defaults */ + return; + } + if(!section->option.client) + section->option.cert=1; /* Server always needs a certificate */ + context_init(section); /* initialize SSL context */ + + if(section==&local_options) { /* inetd mode */ + if(section->option.accept) + config_error(filename, line_number, + "accept is not allowed in inetd mode"); + /* TODO: some additional checks could be useful + if((unsigned int)section->option.program + + (unsigned int)section->option.remote != 1) + config_error(filename, line_number, + "Single endpoint is required in inetd mode"); + */ + return; + } + + /* standalone mode */ +#ifdef USE_WIN32 + if(!section->option.accept || !section->option.remote) +#else + if((unsigned int)section->option.accept + + (unsigned int)section->option.program + + (unsigned int)section->option.remote != 2) +#endif + config_error(filename, line_number, + "Each service section must define exactly two endpoints"); + return; /* All tests passed -- continue program execution */ +} + +static void config_error(char *name, int num, char *str) { + if(!str) /* NULL -> no error */ + return; + s_log(LOG_RAW, "file %s line %d: %s", name, num, str); + die(1); +} + +static char *stralloc(char *str) { /* Allocate static string */ + char *retval; + + retval=calloc(strlen(str)+1, 1); + if(!retval) { + s_log(LOG_RAW, "Fatal memory allocation error"); + die(2); + } + strcpy(retval, str); + return retval; +} + +#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=stralloc(str); + retval=calloc(max_arg+1, sizeof(char *)); + if(!retval) { + s_log(LOG_RAW, "Fatal memory allocation error"); + die(2); + } + i=0; + while(*ptr && iname; ++fl) { + if(!strcasecmp(fl->name, string)) { + options.facility = fl->value; + break; + } + } + if(options.facility==-1) + return 0; /* 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') { + options.debug_level=*string-'0'; + return 1; /* OK */ + } + options.debug_level=8; /* illegal level */ + for(fl=levels; fl->name; ++fl) { + if(!strcasecmp(fl->name, string)) { + options.debug_level=fl->value; + break; + } + } + if (options.debug_level==8) + return 0; /* FAILED */ + return 1; /* OK */ +} + +/* Parse out SSL options stuff */ + +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}, + {"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}, + {"DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS}, +#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 + {"NO_SESSION_RESUMPTION_ON_RENEGOTIATION", + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION}, +#ifdef SSL_OP_NO_COMPRESSION + {"NO_COMPRESSION", SSL_OP_NO_COMPRESSION}, +#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}, + {"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE}, + {"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 + {"ALL", SSL_OP_ALL}, + {NULL, 0} + }, *option; + + for(option=ssl_opts; option->name; ++option) + if(!strcasecmp(option->name, arg)) + return option->value; + return 0; /* FAILED */ +} + +/* Parse out the socket options stuff */ + +static int on=1; + +#define DEF_VALS {NULL, NULL, NULL} +#define DEF_ACCEPT {(void *)&on, NULL, NULL} + +SOCK_OPT sock_opts[] = { + {"SO_DEBUG", SOL_SOCKET, SO_DEBUG, TYPE_FLAG, DEF_VALS}, + {"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, TYPE_FLAG, DEF_VALS}, + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, TYPE_FLAG, DEF_VALS}, + {"SO_LINGER", SOL_SOCKET, SO_LINGER, TYPE_LINGER, DEF_VALS}, + {"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, TYPE_FLAG, DEF_VALS}, + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, TYPE_INT, DEF_VALS}, + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, TYPE_INT, DEF_VALS}, +#ifdef SO_RCVLOWAT + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, TYPE_INT, DEF_VALS}, +#endif +#ifdef SO_SNDLOWAT + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, TYPE_INT, DEF_VALS}, +#endif +#ifdef SO_RCVTIMEO + {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, TYPE_TIMEVAL, DEF_VALS}, +#endif +#ifdef SO_SNDTIMEO + {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, TYPE_TIMEVAL, DEF_VALS}, +#endif + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, DEF_ACCEPT}, +#ifdef SO_BINDTODEVICE + {"SO_BINDTODEVICE", SOL_SOCKET, SO_BINDTODEVICE, TYPE_STRING, DEF_VALS}, +#endif +#ifdef TCP_KEEPCNT + {"TCP_KEEPCNT", SOL_TCP, TCP_KEEPCNT, TYPE_INT, DEF_VALS}, +#endif +#ifdef TCP_KEEPIDLE + {"TCP_KEEPIDLE", SOL_TCP, TCP_KEEPIDLE, TYPE_INT, DEF_VALS}, +#endif +#ifdef TCP_KEEPINTVL + {"TCP_KEEPINTVL", SOL_TCP, TCP_KEEPINTVL, TYPE_INT, DEF_VALS}, +#endif +#ifdef IP_TOS + {"IP_TOS", IPPROTO_IP, IP_TOS, TYPE_INT, DEF_VALS}, +#endif +#ifdef IP_TTL + {"IP_TTL", IPPROTO_IP, IP_TTL, TYPE_INT, DEF_VALS}, +#endif +#ifdef IP_MAXSEG + {"TCP_MAXSEG", IPPROTO_TCP, TCP_MAXSEG, TYPE_INT, DEF_VALS}, +#endif + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, TYPE_FLAG, DEF_VALS}, + {NULL, 0, 0, TYPE_NONE, DEF_VALS} +}; + +static int print_socket_options(void) { + int fd; + socklen_t optlen; + SOCK_OPT *ptr; + OPT_UNION val; + char line[STRLEN]; + + fd=socket(AF_INET, SOCK_STREAM, 0); + + s_log(LOG_RAW, "Socket option defaults:"); + s_log(LOG_RAW, " %-16s%-10s%-10s%-10s%-10s", + "Option", "Accept", "Local", "Remote", "OS default"); + for(ptr=sock_opts; ptr->opt_str; ++ptr) { + /* display option name */ + sprintf(line, " %-16s", ptr->opt_str); + /* display stunnel default values */ + print_option(line, ptr->opt_type, ptr->opt_val[0]); + print_option(line, ptr->opt_type, ptr->opt_val[1]); + print_option(line, ptr->opt_type, ptr->opt_val[2]); + /* display OS default value */ + optlen=sizeof val; + if(getsockopt(fd, ptr->opt_level, + ptr->opt_name, (void *)&val, &optlen)) { + if(get_last_socket_error()!=ENOPROTOOPT) { + s_log(LOG_RAW, "%s", line); /* dump the name and assigned values */ + sockerror("getsockopt"); + return 0; /* FAILED */ + } + safeconcat(line, " -- "); /* write-only value */ + } else + print_option(line, ptr->opt_type, &val); + s_log(LOG_RAW, "%s", line); + } + return 1; /* OK */ +} + +static void print_option(char *line, int type, OPT_UNION *val) { + char text[STRLEN]; + + if(!val) { + safecopy(text, " -- "); + } else { + switch(type) { + case TYPE_FLAG: + case TYPE_INT: + sprintf(text, "%10d", val->i_val); + break; + case TYPE_LINGER: + sprintf(text, "%d:%-8d", + val->linger_val.l_onoff, val->linger_val.l_linger); + break; + case TYPE_TIMEVAL: + sprintf(text, "%6d:%-3d", + (int)val->timeval_val.tv_sec, (int)val->timeval_val.tv_usec); + break; + case TYPE_STRING: + sprintf(text, "%10s", val->c_val); + break; + default: + safecopy(text, " Ooops? "); /* Internal error? */ + } + } + safeconcat(line, text); +} + +static int parse_socket_option(char *arg) { + int socket_type; /* 0-accept, 1-local, 2-remote */ + char *opt_val_str, *opt_val2_str; + SOCK_OPT *ptr; + + if(arg[1]!=':') + return 0; /* FAILED */ + switch(arg[0]) { + case 'a': + socket_type=0; break; + case 'l': + socket_type=1; break; + case 'r': + socket_type=2; break; + default: + return 0; /* FAILED */ + } + arg+=2; + opt_val_str=strchr(arg, '='); + if(!opt_val_str) /* No '='? */ + return 0; /* FAILED */ + *opt_val_str++='\0'; + ptr=sock_opts; + for(;;) { + if(!ptr->opt_str) + return 0; /* FAILED */ + if(!strcmp(arg, ptr->opt_str)) + break; /* option name found */ + ++ptr; + } + ptr->opt_val[socket_type]=calloc(1, sizeof(OPT_UNION)); + switch(ptr->opt_type) { + case TYPE_FLAG: + case TYPE_INT: + ptr->opt_val[socket_type]->i_val=atoi(opt_val_str); + return 1; /* 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=atoi(opt_val2_str); + } else { + ptr->opt_val[socket_type]->linger_val.l_linger=0; + } + ptr->opt_val[socket_type]->linger_val.l_onoff=atoi(opt_val_str); + return 1; /* 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=atoi(opt_val2_str); + } else { + ptr->opt_val[socket_type]->timeval_val.tv_usec=0; + } + ptr->opt_val[socket_type]->timeval_val.tv_sec=atoi(opt_val_str); + return 1; /* OK */ + case TYPE_STRING: + if(strlen(opt_val_str)+1>sizeof(OPT_UNION)) + return 0; /* FAILED */ + strcpy(ptr->opt_val[socket_type]->c_val, opt_val_str); + return 1; /* OK */ + default: + ; /* ANSI C compiler needs it */ + } + return 0; /* FAILED */ +} + +/* Parse out OCSP URL */ + +static char *parse_ocsp_url(LOCAL_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(!hostport2addrlist(§ion->ocsp_addr, host, port)) + return "Failed to resolve OCSP server address"; + section->ocsp_path=stralloc(path); + if(host) + OPENSSL_free(host); + if(port) + OPENSSL_free(port); + if(path) + OPENSSL_free(path); + return NULL; /* OK! */ +} + +/* Parse out OCSP flags stuff */ + +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 */ +} + +/* End of options.c */ --- a/src/prototypes.h +++ b/src/prototypes.h @@ -231,6 +231,7 @@ typedef struct local_options { unsigned int remote:1; unsigned int retry:1; /* loop remote+program */ unsigned int sessiond:1; + unsigned int xforwardedfor:1; #ifndef USE_WIN32 unsigned int program:1; unsigned int pty:1; @@ -334,6 +335,8 @@ typedef struct { FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ s_poll_set fds; /* File descriptors */ + int buffsize; /* current buffer size, may be lower than BUFFSIZE */ + int crlf_seen; /* the number of successive CRLF seen */ } CLI; extern int max_clients; --- /dev/null +++ b/src/prototypes.h.orig @@ -0,0 +1,470 @@ +/* + * stunnel Universal SSL tunnel + * Copyright (C) 1998-2009 Michal Trojnara + * + * 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 . + * + * 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. + */ + +#ifndef PROTOTYPES_H +#define PROTOTYPES_H + +#include "common.h" + +/**************************************** Network data structure */ + +#define MAX_HOSTS 16 + +typedef union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in in; +#if defined(USE_IPv6) + struct sockaddr_in6 in6; +#endif +} SOCKADDR_UNION; + +typedef struct sockaddr_list { /* list of addresses */ + SOCKADDR_UNION addr[MAX_HOSTS]; /* the list of addresses */ + u16 cur; /* current address for round-robin */ + u16 num; /* how many addresses are used */ +} SOCKADDR_LIST; + +#ifdef __INNOTEK_LIBC__ +#define socklen_t __socklen_t +#define strcasecmp stricmp +#define strncasecmp strnicmp +#define NI_NUMERICHOST 1 +#define NI_NUMERICSERV 2 +#endif + + +/**************************************** Prototypes for stunnel.c */ + +extern volatile int num_clients; + +void main_initialize(char *, char *); +void main_execute(void); +#if !defined (USE_WIN32) && !defined (__vms) && !defined(USE_OS2) +void drop_privileges(void); +#endif +void stunnel_info(int); +void die(int); + +/**************************************** Prototypes for log.c */ + +void log_open(void); +void log_close(void); +void log_flush(void); +void s_log(int, const char *, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))); +#else + ; +#endif +void ioerror(const char *); +void sockerror(const char *); +void log_error(int, int, const char *); +char *my_strerror(int); + +/**************************************** Prototypes for pty.c */ +/* Based on Public Domain code by Tatu Ylonen */ + +int pty_allocate(int *, int *, char *, int); +#if 0 +void pty_release(char *); +void pty_make_controlling_tty(int *, char *); +#endif + +/**************************************** Prototypes for ssl.c */ + +typedef enum { + COMP_NONE, COMP_ZLIB, COMP_RLE +} COMP_TYPE; + +extern int cli_index, opt_index;; + +void ssl_init(void); +void ssl_configure(void); +#ifdef HAVE_OSSL_ENGINE_H +void open_engine(const char *); +void ctrl_engine(const char *, const char *); +void close_engine(void); +ENGINE *get_engine(int); +#endif + +/**************************************** Prototypes for options.c */ + +typedef struct { + /* some data for SSL initialization in ssl.c */ + COMP_TYPE compression; /* compression type */ + char *egd_sock; /* entropy gathering daemon socket */ + char *rand_file; /* file with random data */ + int random_bytes; /* how many random bytes to read */ + + /* some global data for stunnel.c */ +#ifndef USE_WIN32 +#ifdef HAVE_CHROOT + char *chroot_dir; +#endif + unsigned long dpid; + char *pidfile; + int uid, gid; +#endif + + /* Win32 specific data for gui.c */ +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + char *win32_service; +#endif + + /* logging-support data for log.c */ + int debug_level; /* debug level for logging */ +#ifndef USE_WIN32 + int facility; /* debug facility for syslog */ +#endif + char *output_file; + + /* on/off switches */ + struct { + unsigned int rand_write:1; /* overwrite rand_file */ +#ifdef USE_WIN32 + unsigned int taskbar:1; /* enable the taskbar icon */ +#else /* !USE_WIN32 */ + unsigned int foreground:1; + unsigned int syslog:1; +#endif +#ifdef USE_FIPS + unsigned int fips:1; /* enable FIPS 140-2 mode */ +#endif + } option; +} GLOBAL_OPTIONS; + +extern GLOBAL_OPTIONS options; + +typedef struct local_options { + SSL_CTX *ctx; /* SSL context */ + X509_STORE *revocation_store; /* cert store for CRL checking */ +#ifdef HAVE_OSSL_ENGINE_H + ENGINE *engine; /* engine to read the private key */ +#endif + struct local_options *next; /* next node in the services list */ + char *servname; /* service name for logging & permission checking */ + SSL_SESSION *session; /* jecently used session */ + char local_address[IPLEN]; /* dotted-decimal address to bind */ +#ifndef USE_FORK + int stack_size; /* stack size for this thread */ +#endif + + /* service-specific data for ctx.c */ + char *ca_dir; /* directory for hashed certs */ + char *ca_file; /* file containing bunches of certs */ + char *crl_dir; /* directory for hashed CRLs */ + char *crl_file; /* file containing bunches of CRLs */ + char *cipher_list; + char *cert; /* cert filename */ + char *key; /* pem (priv key/cert) filename */ + long session_timeout; + int verify_level; + int verify_use_only_my; + long ssl_options; +#if SSLEAY_VERSION_NUMBER >= 0x00907000L + SOCKADDR_LIST ocsp_addr; + char *ocsp_path; + unsigned long ocsp_flags; +#endif /* OpenSSL-0.9.7 */ + SSL_METHOD *client_method, *server_method; + SOCKADDR_LIST sessiond_addr; + + /* service-specific data for client.c */ + int fd; /* file descriptor accepting connections for this service */ + char *execname, **execargs; /* program name and arguments for local mode */ + SOCKADDR_LIST local_addr, remote_addr, source_addr; + char *username; + char *remote_address; + int timeout_busy; /* maximum waiting for data time */ + int timeout_close; /* maximum close_notify time */ + int timeout_connect; /* maximum connect() time */ + int timeout_idle; /* maximum idle connection time */ + enum {FAILOVER_RR, FAILOVER_PRIO} failover; /* failover strategy */ + + /* protocol name for protocol.c */ + char *protocol; + char *protocol_host; + char *protocol_username; + char *protocol_password; + char *protocol_authentication; + + /* on/off switches */ + struct { + unsigned int cert:1; + unsigned int client:1; + unsigned int delayed_lookup:1; + unsigned int accept:1; + unsigned int remote:1; + unsigned int retry:1; /* loop remote+program */ + unsigned int sessiond:1; +#ifndef USE_WIN32 + unsigned int program:1; + unsigned int pty:1; + unsigned int transparent:1; +#endif +#if SSLEAY_VERSION_NUMBER >= 0x00907000L + unsigned int ocsp:1; +#endif + } option; +} LOCAL_OPTIONS; + +extern LOCAL_OPTIONS local_options; + +typedef enum { + TYPE_NONE, TYPE_FLAG, TYPE_INT, TYPE_LINGER, TYPE_TIMEVAL, TYPE_STRING +} VAL_TYPE; + +typedef union { + int i_val; + long l_val; + char c_val[16]; + struct linger linger_val; + struct timeval timeval_val; +} OPT_UNION; + +typedef struct { + char *opt_str; + int opt_level; + int opt_name; + VAL_TYPE opt_type; + OPT_UNION *opt_val[3]; +} SOCK_OPT; + +void parse_config(char *, char *); + +/**************************************** Prototypes for ctx.c */ + +void context_init(LOCAL_OPTIONS *); +void sslerror(char *); + +/**************************************** Prototypes for verify.c */ + +void verify_init(LOCAL_OPTIONS *); + +/**************************************** Prototypes for network.c */ + +#ifdef USE_POLL +#define MAX_FD 256 +#endif + +typedef struct { +#ifdef USE_POLL + struct pollfd ufds[MAX_FD]; + unsigned int nfds; +#else + fd_set irfds, iwfds, orfds, owfds; + int max; +#endif +} s_poll_set; + +void s_poll_init(s_poll_set *); +void s_poll_add(s_poll_set *, int, int, int); +int s_poll_canread(s_poll_set *, int); +int s_poll_canwrite(s_poll_set *, int); +int s_poll_wait(s_poll_set *, int, int); + +#ifndef USE_WIN32 +int signal_pipe_init(void); +void child_status(void); /* dead libwrap or 'exec' process detected */ +#endif +int set_socket_options(int, int); +int alloc_fd(int); +void setnonblock(int, unsigned long); + +/**************************************** Prototypes for client.c */ + +typedef struct { + int fd; /* File descriptor */ + int rd; /* Open for read */ + int wr; /* Open for write */ + int is_socket; /* File descriptor is a socket */ +} FD; + +typedef struct { + LOCAL_OPTIONS *opt; + char accepted_address[IPLEN]; /* text */ + SOCKADDR_LIST peer_addr; /* Peer address */ + FD local_rfd, local_wfd; /* Read and write local descriptors */ + FD remote_fd; /* Remote file descriptor */ + SSL *ssl; /* SSL Connection */ + SOCKADDR_LIST bind_addr; + /* IP for explicit local bind or transparent proxy */ + unsigned long pid; /* PID of local process */ + int fd; /* Temporary file descriptor */ + jmp_buf err; + + char sock_buff[BUFFSIZE]; /* Socket read buffer */ + char ssl_buff[BUFFSIZE]; /* SSL read buffer */ + int sock_ptr, ssl_ptr; /* Index of first unused byte in buffer */ + FD *sock_rfd, *sock_wfd; /* Read and write socket descriptors */ + FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ + int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ + s_poll_set fds; /* File descriptors */ +} CLI; + +extern int max_clients; +#ifndef USE_WIN32 +extern int max_fds; +#endif + +CLI *alloc_client_session(LOCAL_OPTIONS *, int, int); +void *client(void *); + +/**************************************** Prototypes for network.c */ + +int connect_blocking(CLI *, SOCKADDR_UNION *, socklen_t); +void write_blocking(CLI *, int fd, void *, int); +void read_blocking(CLI *, int fd, void *, int); +void fdputline(CLI *, int, const char *); +void fdgetline(CLI *, int, char *); +/* descriptor versions of fprintf/fscanf */ +int fdprintf(CLI *, int, const char *, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))); +#else + ; +#endif +int fdscanf(CLI *, int, const char *, char *) +#ifdef __GNUC__ + __attribute__ ((format (scanf, 3, 0))); +#else + ; +#endif + +/**************************************** Prototype for protocol.c */ + +void negotiate(CLI *c); + +/**************************************** Prototypes for resolver.c */ + +int name2addrlist(SOCKADDR_LIST *, char *, char *); +int hostport2addrlist(SOCKADDR_LIST *, char *, char *); +char *s_ntop(char *, SOCKADDR_UNION *); + +/**************************************** Prototypes for sthreads.c */ + +typedef enum { + CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, + CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS +} SECTION_CODE; + +void enter_critical_section(SECTION_CODE); +void leave_critical_section(SECTION_CODE); +void sthreads_init(void); +unsigned long stunnel_process_id(void); +unsigned long stunnel_thread_id(void); +int create_client(int, int, CLI *, void *(*)(void *)); +#ifdef USE_UCONTEXT +typedef struct CONTEXT_STRUCTURE { + char *stack; /* CPU stack for this thread */ + unsigned long id; + ucontext_t ctx; + s_poll_set *fds; + int ready; /* number of ready file descriptors */ + time_t finish; /* when to finish poll() for this context */ + struct CONTEXT_STRUCTURE *next; /* next context on a list */ +} CONTEXT; +extern CONTEXT *ready_head, *ready_tail; +extern CONTEXT *waiting_head, *waiting_tail; +#endif +#ifdef _WIN32_WCE +int _beginthread(void (*)(void *), int, void *); +void _endthread(void); +#endif +#ifdef DEBUG_STACK_SIZE +void stack_info(int); +#endif + +/**************************************** Prototypes for gui.c */ + +typedef struct { + LOCAL_OPTIONS *section; + char pass[PEM_BUFSIZE]; +} UI_DATA; + +#ifdef USE_WIN32 +void win_log(char *); +void exit_win32(int); +int passwd_cb(char *, int, int, void *); +#ifdef HAVE_OSSL_ENGINE_H +int pin_cb(UI *, UI_STRING *); +#endif + +#ifndef _WIN32_WCE +typedef int (CALLBACK * GETADDRINFO) (const char *, + const char *, const struct addrinfo *, struct addrinfo **); +typedef void (CALLBACK * FREEADDRINFO) (struct addrinfo FAR *); +typedef int (CALLBACK * GETNAMEINFO) (const struct sockaddr *, socklen_t, + char *, size_t, char *, size_t, int); +extern GETADDRINFO s_getaddrinfo; +extern FREEADDRINFO s_freeaddrinfo; +extern GETNAMEINFO s_getnameinfo; +#endif /* ! _WIN32_WCE */ +#endif /* USE_WIN32 */ + +/**************************************** Prototypes for file.c */ + +typedef struct disk_file { +#ifdef USE_WIN32 + HANDLE fh; +#else + int fd; +#endif + /* the inteface is prepared to easily implement buffering if needed */ +} DISK_FILE; + +#ifndef USE_WIN32 +DISK_FILE *file_fdopen(int); +#endif +DISK_FILE *file_open(char *, int); +void file_close(DISK_FILE *); +int file_getline(DISK_FILE *, char *, int); +int file_putline(DISK_FILE *, char *); + +#ifdef USE_WIN32 +LPTSTR str2tstr(const LPSTR); +LPSTR tstr2str(const LPTSTR); +#endif + +/**************************************** Prototypes for libwrap.c */ + +void libwrap_init(int); +void auth_libwrap(CLI *); + +#endif /* defined PROTOTYPES_H */ + +/* End of prototypes.h */