Skip to content

Instantly share code, notes, and snippets.

@grantc
Last active December 28, 2018 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save grantc/bbaa657f00191be1e88b to your computer and use it in GitHub Desktop.
Save grantc/bbaa657f00191be1e88b to your computer and use it in GitHub Desktop.
X-Forwarded-For patch for Stunnel 5.22
diff -rupN stunnel-5.22.orig/doc/stunnel.8.in stunnel-5.22/doc/stunnel.8.in
--- stunnel-5.22.orig/doc/stunnel.8.in 2015-07-27 09:54:55.000000000 +0000
+++ stunnel-5.22/doc/stunnel.8.in 2015-08-18 09:25:06.868928121 +0000
@@ -964,6 +964,10 @@ This option has been renamed to \fInone\
.RE
.RS 4
.RE
+.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.
.IP "\fBverify\fR = \s-1LEVEL\s0" 4
.IX Item "verify = LEVEL"
verify the peer certificate
diff -rupN stunnel-5.22.orig/src/client.c stunnel-5.22/src/client.c
--- stunnel-5.22.orig/src/client.c 2015-07-22 13:24:31.000000000 +0000
+++ stunnel-5.22/src/client.c 2015-08-18 09:25:06.868928121 +0000
@@ -48,6 +48,8 @@
#define SHUT_RDWR 2
#endif
+#define IPLEN 40
+
NOEXPORT void client_try(CLI *);
NOEXPORT void client_run(CLI *);
NOEXPORT void local_start(CLI *);
@@ -74,6 +76,12 @@ CLI *alloc_client_session(SERVICE_OPTION
c=str_alloc_detached(sizeof(CLI));
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;
c->redirect=REDIRECT_OFF;
@@ -525,6 +533,28 @@ NOEXPORT void new_chain(CLI *c) {
s_log(LOG_DEBUG, "Peer certificate was cached (%d bytes)", len);
}
+/** 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));
+}
+
/****************************** transfer data */
NOEXPORT void transfer(CLI *c) {
int watchdog=0; /* a counter to detect an infinite loop */
@@ -549,7 +579,7 @@ NOEXPORT void transfer(CLI *c) {
do { /* main loop of client data transfer */
/****************************** initialize *_wants_* */
read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)
- && c->ssl_ptr<BUFFSIZE && !read_wants_write;
+ && c->ssl_ptr<c->buffsize && !read_wants_write;
write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN)
&& c->sock_ptr && !write_wants_read;
@@ -558,7 +588,7 @@ NOEXPORT void transfer(CLI *c) {
/* for plain socket open data strem = open file descriptor */
/* make sure to add each open socket to receive exceptions! */
if(sock_open_rd) /* only poll if the read file descriptor is open */
- s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0);
+ s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<c->buffsize, 0);
if(sock_open_wr) /* only poll if the write file descriptor is open */
s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr>0);
/* poll SSL file descriptors unless SSL shutdown was completed */
@@ -673,7 +703,7 @@ NOEXPORT void transfer(CLI *c) {
/****************************** read from socket */
if(sock_open_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, c->buffsize-c->sock_ptr);
switch(num) {
case -1:
if(parse_socket_error(c, "readsocket"))
@@ -693,7 +723,7 @@ NOEXPORT void transfer(CLI *c) {
/****************************** update *_wants_* based on new *_ptr */
/* this update is also required for SSL_pending() to be used */
read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)
- && c->ssl_ptr<BUFFSIZE && !read_wants_write;
+ && c->ssl_ptr<c->buffsize && !read_wants_write;
write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN)
&& c->sock_ptr && !write_wants_read;
diff -rupN stunnel-5.22.orig/src/common.h stunnel-5.22/src/common.h
--- stunnel-5.22.orig/src/common.h 2015-06-01 14:25:32.000000000 +0000
+++ stunnel-5.22/src/common.h 2015-08-18 09:25:06.869928114 +0000
@@ -51,6 +51,9 @@
/* I/O buffer size: 18432 (0x4800) is the maximum size of SSL record payload */
#define BUFFSIZE 18432
+/* maximum space reserved for header insertion in BUFFSIZE */
+#define BUFF_RESERVED 1024
+
/* 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
diff -rupN stunnel-5.22.orig/src/options.c stunnel-5.22/src/options.c
--- stunnel-5.22.orig/src/options.c 2015-07-16 11:19:07.000000000 +0000
+++ stunnel-5.22/src/options.c 2015-08-18 09:25:06.870928107 +0000
@@ -1658,6 +1658,33 @@ NOEXPORT char *parse_service_option(CMD
#endif /* !defined(OPENSSL_NO_ENGINE) */
+ /* xforwardedfor */
+ switch(cmd) {
+ case CMD_BEGIN:
+ 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_END:
+ break;
+ case CMD_FREE:
+ break;
+ case CMD_DEFAULT:
+ break;
+ case CMD_HELP:
+ s_log(LOG_NOTICE, "%-22s = yes|no append an HTTP X-Forwarded-For header",
+ "xforwardedfor");
+ break;
+ }
+
/* exec */
switch(cmd) {
case CMD_BEGIN:
diff -rupN stunnel-5.22.orig/src/prototypes.h stunnel-5.22/src/prototypes.h
--- stunnel-5.22.orig/src/prototypes.h 2015-07-22 13:24:31.000000000 +0000
+++ stunnel-5.22/src/prototypes.h 2015-08-18 09:25:06.871928100 +0000
@@ -271,6 +271,7 @@ typedef struct service_options_struct {
unsigned accept:1; /* endpoint: accept */
unsigned client:1;
unsigned delayed_lookup:1;
+ unsigned int xforwardedfor:1;
#ifdef USE_LIBWRAP
unsigned libwrap:1;
#endif
@@ -395,6 +396,8 @@ typedef struct {
uint64_t sock_bytes, ssl_bytes; /* bytes written to socket and SSL */
s_poll_set *fds; /* file descriptors */
uintptr_t redirect; /* redirect to another destination after failed auth */
+ int buffsize; /* current buffer size, may be lower than BUFFSIZE */
+ int crlf_seen; /* the number of successive CRLF seen */
} CLI;
/**************************************** prototypes for stunnel.c */
diff -rupN stunnel-5.22.orig/tools/stunnel.init.redhat stunnel-5.22/tools/stunnel.init.redhat
--- stunnel-5.22.orig/tools/stunnel.init.redhat 1970-01-01 00:00:00.000000000 +0000
+++ stunnel-5.22/tools/stunnel.init.redhat 2015-08-18 09:35:40.848726158 +0000
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# stunnel Start/Stop the stunnel daemons
+#
+# description: stunnel is a script that runs stunnel daemons
+# version 1.00
+#
+# chkconfig: 345 40 60
+#
+# processname: stunnel
+# pidfile: /var/run/stunnel/stunnel.pid
+#
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+servicename=stunnel
+processname=stunnel
+pidfile=/var/run/stunnel/stunnel.pid
+
+RETVAL=0
+
+start() {
+
+ echo -n "Starting stunnel services: "
+ daemon --check $servicename '/usr/sbin/stunnel /etc/stunnel/stunnel.conf &>/dev/null'
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$servicename
+
+}
+
+stop() {
+
+ echo -n "Stopping stunnel services: "
+ killproc -p $pidfile $servicename
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$servicename
+
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status -p $pidfile $processname
+ RETVAL=$?
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f /var/lock/subsys/$servicename ]; then
+ stop
+ start
+ fi
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart}"
+ RETVAL=1
+ ;;
+esac
+
+exit $RETVAL
+
diff -rupN stunnel-5.22.orig/tools/stunnel.spec stunnel-5.22/tools/stunnel.spec
--- stunnel-5.22.orig/tools/stunnel.spec 2015-07-27 09:51:20.000000000 +0000
+++ stunnel-5.22/tools/stunnel.spec 2015-08-18 10:54:37.765604102 +0000
@@ -7,11 +7,13 @@ Version: 5.22
Release: 1
License: GPL with an OpenSSL exception
Group: Applications/Networking
-Source: stunnel-%{version}.tar.gz
+Source0: stunnel-%{version}.tar.gz
+Source1: stunnel.init.redhat
Packager: Bill Quayle <Bill.Quayle@citadel.com>
Requires: openssl >= 0.9.7
BuildRequires: openssl-devel >= 0.9.7
Buildroot: /var/tmp/stunnel-%{version}-root
+Patch0: stunnel-5.22.xforwardfor.patch
%description
The stunnel program is designed to work as SSL encryption wrapper
@@ -27,6 +29,7 @@ changes to the source code.
%prep
%setup -n stunnel-%{version}
+%patch0 -p1
%build
if [ ! -x ./configure ]; then
@@ -51,7 +54,7 @@ CFLAGS="%{optflags}" ./configure --prefi
%{__install} -m755 src/.libs/libstunnel.la %{buildroot}%{_libdir}
%{__install} -m644 doc/stunnel.8 %{buildroot}%{_mandir}/man8/stunnel.8
%{__install} -m644 tools/stunnel.conf-sample %{buildroot}%{_sysconfdir}/stunnel
-%{__install} -m500 tools/stunnel.init %{buildroot}%{_initrddir}/stunnel
+%{__install} -m500 %SOURCE1 %{buildroot}%{_initrddir}/stunnel
%clean
%{__rm} -rf %{buildroot}
@roytam1
Copy link

roytam1 commented Dec 28, 2018

it seems that this part is missing?

@@ -590,10 +619,72 @@ static void transfer(CLI *c) {
                  * writesocket() above made some room in c->ssl_buff */
                 (read_wants_write && ssl_can_wr)) {
             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, 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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment