7f2ef75144
git-svn-id: svn://svn.openwrt.org/openwrt/packages@19859 3c298f89-4303-0410-b956-a3cf2f4a3e73
4928 lines
159 KiB
Diff
4928 lines
159 KiB
Diff
--- 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 [<filename>] | \-fd n | \-help | \-version | \-sockets
|
||
+.IP "\fB\s-1WIN32:\s0\fR" 4
|
||
+.IX Item "WIN32:"
|
||
+\&\fBstunnel\fR [ [\-install | \-uninstall | \-start | \-stop]
|
||
+ [\-quiet] [<filename>] ] | \-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 "<filename>"
|
||
+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 | <engine id>" 4
|
||
+.IX Item "engine = auto | <engine id>"
|
||
+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<connect> option) on Linux >=2.6.28
|
||
+\& remote mode (I<connect> option) 2.2.x
|
||
+\& local mode (I<exec> 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<71>
|
||
.Sp
|
||
Actuellement g<>r<EFBFBD>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<71>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 <20>\ exec\ <20>
|
||
--- 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 <buffer> between positions <start> and <stop>
|
||
+ * to insert <string> of length <len>. <start> and <stop> are updated to their
|
||
+ * new respective values, and the number of characters inserted is returned.
|
||
+ * If <len> is too long, nothing is done and -1 is returned.
|
||
+ * Note that neither <string> nor <buffer> 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_ptr<BUFFSIZE && !SSL_read_wants_write;
|
||
+ //ssl_rd && c->ssl_ptr<BUFFSIZE && !SSL_read_wants_write;
|
||
+ ssl_rd && c->ssl_ptr<c->buffsize && !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_ptr<BUFFSIZE)
|
||
+ //if(sock_rd && c->sock_ptr<BUFFSIZE)
|
||
+ if(sock_rd && c->sock_ptr<c->buffsize)
|
||
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. <ssl_ptr> points to the
|
||
+ * first byte of unread data, and <last> 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 <Michal.Trojnara@mirt.net>
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or modify it
|
||
+ * under the terms of the GNU General Public License as published by the
|
||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||
+ * option) any later version.
|
||
+ *
|
||
+ * This program is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ * See the GNU General Public License for more details.
|
||
+ *
|
||
+ * You should have received a copy of the GNU General Public License along
|
||
+ * with this program; if not, see <http://www.gnu.org/licenses>.
|
||
+ *
|
||
+ * Linking stunnel statically or dynamically with other modules is making
|
||
+ * a combined work based on stunnel. Thus, the terms and conditions of
|
||
+ * the GNU General Public License cover the whole combination.
|
||
+ *
|
||
+ * In addition, as a special exception, the copyright holder of stunnel
|
||
+ * gives you permission to combine stunnel with free software programs or
|
||
+ * libraries that are released under the GNU LGPL and with code included
|
||
+ * in the standard release of OpenSSL under the OpenSSL License (or
|
||
+ * modified versions of such code, with unchanged license). You may copy
|
||
+ * and distribute such a system following the terms of the GNU GPL for
|
||
+ * stunnel and the licenses of the other code concerned.
|
||
+ *
|
||
+ * Note that people who make modified versions of stunnel are not obligated
|
||
+ * to grant this special exception for their modified versions; it is their
|
||
+ * choice whether to do so. The GNU General Public License gives permission
|
||
+ * to release a modified version without this exception; this exception
|
||
+ * also makes it possible to release a modified version which carries
|
||
+ * forward this exception.
|
||
+ */
|
||
+
|
||
+/* 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_ptr<BUFFSIZE && !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_ptr<BUFFSIZE)
|
||
+ s_poll_add(&c->fds, 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_try<address_list->num; 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 <Michal.Trojnara@mirt.net>
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or modify it
|
||
+ * under the terms of the GNU General Public License as published by the
|
||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||
+ * option) any later version.
|
||
+ *
|
||
+ * This program is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ * See the GNU General Public License for more details.
|
||
+ *
|
||
+ * You should have received a copy of the GNU General Public License along
|
||
+ * with this program; if not, see <http://www.gnu.org/licenses>.
|
||
+ *
|
||
+ * Linking stunnel statically or dynamically with other modules is making
|
||
+ * a combined work based on stunnel. Thus, the terms and conditions of
|
||
+ * the GNU General Public License cover the whole combination.
|
||
+ *
|
||
+ * In addition, as a special exception, the copyright holder of stunnel
|
||
+ * gives you permission to combine stunnel with free software programs or
|
||
+ * libraries that are released under the GNU LGPL and with code included
|
||
+ * in the standard release of OpenSSL under the OpenSSL License (or
|
||
+ * modified versions of such code, with unchanged license). You may copy
|
||
+ * and distribute such a system following the terms of the GNU GPL for
|
||
+ * stunnel and the licenses of the other code concerned.
|
||
+ *
|
||
+ * Note that people who make modified versions of stunnel are not obligated
|
||
+ * to grant this special exception for their modified versions; it is their
|
||
+ * choice whether to do so. The GNU General Public License gives permission
|
||
+ * to release a modified version without this exception; this exception
|
||
+ * also makes it possible to release a modified version which carries
|
||
+ * forward this exception.
|
||
+ */
|
||
+
|
||
+#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 <starlet.h>
|
||
+#endif /* __vms */
|
||
+
|
||
+/* For nsr-tandem-nsk architecture */
|
||
+#ifdef __TANDEM
|
||
+#include <floss.h>
|
||
+#endif
|
||
+
|
||
+/* threads model */
|
||
+#ifdef USE_UCONTEXT
|
||
+#define __MAKECONTEXT_V2_SOURCE
|
||
+#include <ucontext.h>
|
||
+#endif
|
||
+
|
||
+#ifdef USE_PTHREAD
|
||
+#define THREADS
|
||
+#define _REENTRANT
|
||
+#define _THREAD_SAFE
|
||
+#include <pthread.h>
|
||
+#endif
|
||
+
|
||
+/* TCP wrapper */
|
||
+#if HAVE_TCPD_H && HAVE_LIBWRAP
|
||
+#define USE_LIBWRAP
|
||
+#endif
|
||
+
|
||
+/* Must be included before sys/stat.h for Ultrix */
|
||
+#include <sys/types.h> /* u_short, u_long */
|
||
+/* General headers */
|
||
+#include <stdio.h>
|
||
+/* Must be included before sys/stat.h for Ultrix */
|
||
+#ifndef _WIN32_WCE
|
||
+#include <errno.h>
|
||
+#endif
|
||
+#include <stdlib.h>
|
||
+#include <stdarg.h> /* va_ */
|
||
+#include <string.h>
|
||
+#include <ctype.h> /* isalnum */
|
||
+#include <time.h>
|
||
+#include <sys/stat.h> /* stat */
|
||
+#include <setjmp.h>
|
||
+
|
||
+/**************************************** 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 <winsock.h>
|
||
+#else
|
||
+#include <winsock2.h>
|
||
+#include <ws2tcpip.h>
|
||
+#endif
|
||
+#include <windows.h>
|
||
+
|
||
+#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 <process.h> /* _beginthread */
|
||
+#include <tchar.h>
|
||
+
|
||
+#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 <inet.h>
|
||
+#include <unistd.h>
|
||
+#else /* __vms */
|
||
+#include <syslog.h>
|
||
+#endif /* __vms */
|
||
+
|
||
+ /* Unix-specific headers */
|
||
+#include <signal.h> /* signal */
|
||
+#include <sys/wait.h> /* wait */
|
||
+#ifdef HAVE_SYS_RESOURCE_H
|
||
+#include <sys/resource.h> /* getrlimit */
|
||
+#endif
|
||
+#ifdef HAVE_UNISTD_H
|
||
+#include <unistd.h> /* getpid, fork, execvp, exit */
|
||
+#endif
|
||
+#ifdef HAVE_STROPTS_H
|
||
+#include <stropts.h>
|
||
+#endif
|
||
+#ifdef HAVE_SYS_SELECT_H
|
||
+#include <sys/select.h> /* for aix */
|
||
+#endif
|
||
+
|
||
+#ifndef BROKEN_POLL
|
||
+#ifdef HAVE_POLL_H
|
||
+#include <poll.h>
|
||
+#define USE_POLL
|
||
+#else /* HAVE_POLL_H */
|
||
+#ifdef HAVE_SYS_POLL_H
|
||
+#include <sys/poll.h>
|
||
+#define USE_POLL
|
||
+#endif /* HAVE_SYS_POLL_H */
|
||
+#endif /* HAVE_POLL_H */
|
||
+#endif /* BROKEN_POLL */
|
||
+
|
||
+#ifdef HAVE_SYS_FILIO_H
|
||
+#include <sys/filio.h> /* for FIONBIO */
|
||
+#endif
|
||
+#include <pwd.h>
|
||
+#ifdef HAVE_GRP_H
|
||
+#include <grp.h>
|
||
+#endif
|
||
+#ifdef __BEOS__
|
||
+#include <posix/grp.h>
|
||
+#endif
|
||
+#include <fcntl.h>
|
||
+
|
||
+#include <netinet/in.h> /* struct sockaddr_in */
|
||
+#include <sys/socket.h> /* getpeername */
|
||
+#include <arpa/inet.h> /* inet_ntoa */
|
||
+#include <sys/time.h> /* select */
|
||
+#include <sys/ioctl.h> /* ioctl */
|
||
+#include <netinet/tcp.h>
|
||
+#include <netdb.h>
|
||
+#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 <openssl/opensslconf.h>
|
||
+#if !defined(OPENSSL_THREADS) && defined(USE_PTHREAD)
|
||
+#error OpenSSL library compiled without thread support
|
||
+#endif /* !OPENSSL_THREADS && USE_PTHREAD */
|
||
+
|
||
+#include <openssl/lhash.h>
|
||
+#include <openssl/ssl.h>
|
||
+#include <openssl/err.h>
|
||
+#include <openssl/crypto.h> /* for CRYPTO_* and SSLeay_version */
|
||
+#include <openssl/rand.h>
|
||
+#include <openssl/md4.h>
|
||
+#include <openssl/des.h>
|
||
+
|
||
+#ifdef HAVE_OSSL_ENGINE_H
|
||
+#include <openssl/engine.h>
|
||
+#endif /* HAVE_OSSL_ENGINE_H */
|
||
+
|
||
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
|
||
+#include <openssl/ocsp.h>
|
||
+#endif /* OpenSSL-0.9.7 */
|
||
+
|
||
+#ifdef USE_FIPS
|
||
+#include <openssl/fips.h>
|
||
+#include <openssl/fips_rand.h>
|
||
+#endif /* USE_FIPS */
|
||
+
|
||
+#else /* HAVE_OPENSSL */
|
||
+
|
||
+#include <lhash.h>
|
||
+#include <ssl.h>
|
||
+#include <err.h>
|
||
+#include <crypto.h>
|
||
+#include <md4.h>
|
||
+#include <des.h>
|
||
+
|
||
+#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 <Michal.Trojnara@mirt.net>
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or modify it
|
||
+ * under the terms of the GNU General Public License as published by the
|
||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||
+ * option) any later version.
|
||
+ *
|
||
+ * This program is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ * See the GNU General Public License for more details.
|
||
+ *
|
||
+ * You should have received a copy of the GNU General Public License along
|
||
+ * with this program; if not, see <http://www.gnu.org/licenses>.
|
||
+ *
|
||
+ * Linking stunnel statically or dynamically with other modules is making
|
||
+ * a combined work based on stunnel. Thus, the terms and conditions of
|
||
+ * the GNU General Public License cover the whole combination.
|
||
+ *
|
||
+ * In addition, as a special exception, the copyright holder of stunnel
|
||
+ * gives you permission to combine stunnel with free software programs or
|
||
+ * libraries that are released under the GNU LGPL and with code included
|
||
+ * in the standard release of OpenSSL under the OpenSSL License (or
|
||
+ * modified versions of such code, with unchanged license). You may copy
|
||
+ * and distribute such a system following the terms of the GNU GPL for
|
||
+ * stunnel and the licenses of the other code concerned.
|
||
+ *
|
||
+ * Note that people who make modified versions of stunnel are not obligated
|
||
+ * to grant this special exception for their modified versions; it is their
|
||
+ * choice whether to do so. The GNU General Public License gives permission
|
||
+ * to release a modified version without this exception; this exception
|
||
+ * also makes it possible to release a modified version which carries
|
||
+ * forward this exception.
|
||
+ */
|
||
+
|
||
+#include "common.h"
|
||
+#include "prototypes.h"
|
||
+
|
||
+#if defined(_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
|
||
+ "[<filename>] ] "
|
||
+#ifndef USE_WIN32
|
||
+ "-fd <n> "
|
||
+#endif
|
||
+ "| -help | -version | -sockets");
|
||
+ s_log(LOG_RAW, " <filename> - 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 <n> - 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 && i<max_arg) {
|
||
+ retval[i++]=ptr;
|
||
+ while(*ptr && !isspace((unsigned char)*ptr))
|
||
+ ++ptr;
|
||
+ while(*ptr && isspace((unsigned char)*ptr))
|
||
+ *ptr++='\0';
|
||
+ }
|
||
+ retval[i]=NULL; /* to show that it's null-terminated */
|
||
+ return retval;
|
||
+}
|
||
+#endif
|
||
+
|
||
+/* Parse out the facility/debug level stuff */
|
||
+
|
||
+typedef struct {
|
||
+ char *name;
|
||
+ int value;
|
||
+} facilitylevel;
|
||
+
|
||
+static int parse_debug_level(char *arg) {
|
||
+ char arg_copy[STRLEN];
|
||
+ char *string;
|
||
+ facilitylevel *fl;
|
||
+
|
||
+/* Facilities only make sense on unix */
|
||
+#if !defined (USE_WIN32) && !defined (__vms)
|
||
+ facilitylevel facilities[] = {
|
||
+ {"auth", LOG_AUTH}, {"cron", LOG_CRON}, {"daemon", LOG_DAEMON},
|
||
+ {"kern", LOG_KERN}, {"lpr", LOG_LPR}, {"mail", LOG_MAIL},
|
||
+ {"news", LOG_NEWS}, {"syslog", LOG_SYSLOG}, {"user", LOG_USER},
|
||
+ {"uucp", LOG_UUCP}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1},
|
||
+ {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4},
|
||
+ {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7},
|
||
+
|
||
+ /* Some that are not on all unicies */
|
||
+#ifdef LOG_AUTHPRIV
|
||
+ {"authpriv", LOG_AUTHPRIV},
|
||
+#endif
|
||
+#ifdef LOG_FTP
|
||
+ {"ftp", LOG_FTP},
|
||
+#endif
|
||
+#ifdef LOG_NTP
|
||
+ {"ntp", LOG_NTP},
|
||
+#endif
|
||
+ {NULL, 0}
|
||
+ };
|
||
+#endif /* USE_WIN32, __vms */
|
||
+
|
||
+ facilitylevel levels[] = {
|
||
+ {"emerg", LOG_EMERG}, {"alert", LOG_ALERT},
|
||
+ {"crit", LOG_CRIT}, {"err", LOG_ERR},
|
||
+ {"warning", LOG_WARNING}, {"notice", LOG_NOTICE},
|
||
+ {"info", LOG_INFO}, {"debug", LOG_DEBUG},
|
||
+ {NULL, -1}
|
||
+ };
|
||
+
|
||
+ safecopy(arg_copy, arg);
|
||
+ string = arg_copy;
|
||
+
|
||
+/* Facilities only make sense on Unix */
|
||
+#if !defined (USE_WIN32) && !defined (__vms)
|
||
+ if(strchr(string, '.')) { /* We have a facility specified */
|
||
+ options.facility=-1;
|
||
+ string=strtok(arg_copy, "."); /* break it up */
|
||
+
|
||
+ for(fl=facilities; fl->name; ++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 <Michal.Trojnara@mirt.net>
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or modify it
|
||
+ * under the terms of the GNU General Public License as published by the
|
||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||
+ * option) any later version.
|
||
+ *
|
||
+ * This program is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ * See the GNU General Public License for more details.
|
||
+ *
|
||
+ * You should have received a copy of the GNU General Public License along
|
||
+ * with this program; if not, see <http://www.gnu.org/licenses>.
|
||
+ *
|
||
+ * Linking stunnel statically or dynamically with other modules is making
|
||
+ * a combined work based on stunnel. Thus, the terms and conditions of
|
||
+ * the GNU General Public License cover the whole combination.
|
||
+ *
|
||
+ * In addition, as a special exception, the copyright holder of stunnel
|
||
+ * gives you permission to combine stunnel with free software programs or
|
||
+ * libraries that are released under the GNU LGPL and with code included
|
||
+ * in the standard release of OpenSSL under the OpenSSL License (or
|
||
+ * modified versions of such code, with unchanged license). You may copy
|
||
+ * and distribute such a system following the terms of the GNU GPL for
|
||
+ * stunnel and the licenses of the other code concerned.
|
||
+ *
|
||
+ * Note that people who make modified versions of stunnel are not obligated
|
||
+ * to grant this special exception for their modified versions; it is their
|
||
+ * choice whether to do so. The GNU General Public License gives permission
|
||
+ * to release a modified version without this exception; this exception
|
||
+ * also makes it possible to release a modified version which carries
|
||
+ * forward this exception.
|
||
+ */
|
||
+
|
||
+#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 <ylo@cs.hut.fi> */
|
||
+
|
||
+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 */
|