From owner-freebsd-ports-bugs@FreeBSD.ORG Mon Aug 5 15:30:00 2013 Return-Path: Delivered-To: freebsd-ports-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id CBBC32C7 for ; Mon, 5 Aug 2013 15:30:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id AB199232E for ; Mon, 5 Aug 2013 15:30:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.7/8.14.7) with ESMTP id r75FU0LW089559 for ; Mon, 5 Aug 2013 15:30:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.7/8.14.7/Submit) id r75FU0o4089558; Mon, 5 Aug 2013 15:30:00 GMT (envelope-from gnats) Resent-Date: Mon, 5 Aug 2013 15:30:00 GMT Resent-Message-Id: <201308051530.r75FU0o4089558@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-ports-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Florian Schürfeld Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id C952BF99 for ; Mon, 5 Aug 2013 15:23:04 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from oldred.freebsd.org (oldred.freebsd.org [8.8.178.121]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id A837C22D2 for ; Mon, 5 Aug 2013 15:23:04 +0000 (UTC) Received: from oldred.freebsd.org ([127.0.1.6]) by oldred.freebsd.org (8.14.5/8.14.7) with ESMTP id r75FN3Le023854 for ; Mon, 5 Aug 2013 15:23:03 GMT (envelope-from nobody@oldred.freebsd.org) Received: (from nobody@localhost) by oldred.freebsd.org (8.14.5/8.14.5/Submit) id r75FN3Mw023774; Mon, 5 Aug 2013 15:23:03 GMT (envelope-from nobody) Message-Id: <201308051523.r75FN3Mw023774@oldred.freebsd.org> Date: Mon, 5 Aug 2013 15:23:03 GMT From: Florian Schürfeld To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: ports/181053: stunnel x-forwared-for patch X-BeenThere: freebsd-ports-bugs@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Ports bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 05 Aug 2013 15:30:00 -0000 >Number: 181053 >Category: ports >Synopsis: stunnel x-forwared-for patch >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-ports-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Aug 05 15:30:00 UTC 2013 >Closed-Date: >Last-Modified: >Originator: Florian Schürfeld >Release: 9.1-p4 >Organization: trivago GmbH >Environment: 9.1-RELEASE-p4 FreeBSD 9.1-RELEASE-p4 #0 r253649: Fri Jul 26 12:10:52 CEST 2013 >Description: stunnel port does not have ability to add x-forwared-for header. There is a patch, but it is not part of the port. >How-To-Repeat: make install >Fix: make fetch make extract apply the patch manually https://gist.github.com/rraptorr/5599557/raw/0f1335bb9bdb37886d1634d27104a51e90d38c1c/stunnel-4.56-xforwarded-for.diff make install Patch attached with submission follows: diff --git a/doc/stunnel.8 b/doc/stunnel.8 index 7624a27..3593f24 100644 --- a/doc/stunnel.8 +++ b/doc/stunnel.8 @@ -753,6 +753,10 @@ This options has been renamed to \fInone\fR. .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 = level" 4 .IX Item "verify = level" verify peer certificate diff --git a/src/client.c b/src/client.c index ad92e2e..7f618e0 100644 --- a/src/client.c +++ b/src/client.c @@ -48,6 +48,8 @@ #define SHUT_RDWR 2 #endif +#define IPLEN 40 + static void client_try(CLI *); static void client_run(CLI *); static void init_local(CLI *); @@ -75,6 +77,12 @@ CLI *alloc_client_session(SERVICE_OPTIONS *opt, int rfd, int wfd) { c=str_alloc(sizeof(CLI)); str_detach(c); 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; @@ -501,6 +509,28 @@ static void new_chain(CLI *c) { } #endif +/* Moves all data from the buffer between positions and + * to insert of length . and are updated to their + * new respective values, and the number of characters inserted is returned. + * If is too long, nothing is done and -1 is returned. + * Note that neither nor can be NULL. + */ +static int buffer_insert_with_len(char *buffer, int *start, int *stop, int limit, char *string, int len) { + if (len > limit - *stop) + return -1; + if (*start > *stop) + return -1; + memmove(buffer + *start + len, buffer + *start, *stop - *start); + memcpy(buffer + *start, string, len); + *start += len; + *stop += len; + return len; +} + +static int buffer_insert(char *buffer, int *start, int *stop, int limit, char *string) { + return buffer_insert_with_len(buffer, start, stop, limit, string, strlen(string)); +} + /****************************** transfer data */ static void transfer(CLI *c) { int watchdog=0; /* a counter to detect an infinite loop */ @@ -519,7 +549,7 @@ static 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_ptrssl_ptrbuffsize && !read_wants_write; write_wants_write=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; @@ -528,7 +558,7 @@ static 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_ptrfds, c->sock_rfd->fd, c->sock_ptrbuffsize, 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); /* poll SSL file descriptors unless SSL shutdown was completed */ @@ -683,7 +713,7 @@ static 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")) @@ -720,7 +750,7 @@ static 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_ptrssl_ptrbuffsize && !read_wants_write; write_wants_write=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; @@ -730,12 +760,70 @@ 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: if(num==0) s_log(LOG_DEBUG, "SSL_read returned 0"); - 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) { + /* X-Forwarded-For: xxxx \r\n\0 */ + char xforw[17 + IPLEN + 3]; + + /* We have all the HTTP headers now. We don't need to + * reserve any space anymore. points to the + * first byte of unread data, and points to the + * exact location where we want to insert our headers, + * which is right before the empty line. + */ + c->buffsize = BUFFSIZE; + + /* 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.sa, + c->peer_addr_len, + 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: diff --git a/src/common.h b/src/common.h index 0efdd7c..8c58f47 100644 --- a/src/common.h +++ b/src/common.h @@ -52,6 +52,9 @@ /* I/O buffer size - 18432 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 --git a/src/options.c b/src/options.c index 0a0e909..f3dff6d 100644 --- a/src/options.c +++ b/src/options.c @@ -1032,6 +1032,33 @@ static char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section, } #endif + /* 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 --git a/src/prototypes.h b/src/prototypes.h index 0b02b54..393c3f7 100644 --- a/src/prototypes.h +++ b/src/prototypes.h @@ -204,6 +204,7 @@ typedef struct service_options_struct { unsigned int accept:1; /* endpoint: accept */ unsigned int client:1; unsigned int delayed_lookup:1; + unsigned int xforwardedfor:1; #ifdef USE_LIBWRAP unsigned int libwrap:1; #endif @@ -433,6 +434,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; CLI *alloc_client_session(SERVICE_OPTIONS *, int, int); >Release-Note: >Audit-Trail: >Unformatted: