diff --git a/Makefile b/Makefile index eba3e9c..5d8e433 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,10 @@ PREFIX = /usr/local BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man +UPSTARTDIR = /etc/init +CONFDIR = /etc/stud +CONFFILE = stud.cfg + CFLAGS = -O2 -g -std=c99 -fno-strict-aliasing -Wall -W -D_GNU_SOURCE -I/usr/local/include LDFLAGS = -lssl -lcrypto -lev -L/usr/local/lib OBJS = stud.o ringbuffer.o configuration.o @@ -45,6 +49,22 @@ install: $(ALL) install -d $(DESTDIR)$(MANDIR)/man8 install -m 644 stud.8 $(DESTDIR)$(MANDIR)/man8 + install -d $(UPSTARTDIR) + install upstart/stud.conf $(UPSTARTDIR) + + install -d $(CONFDIR) + stud --default-config > $(CONFDIR)/$(CONFFILE) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/stud + rm -f $(DESTDIR)$(MANDIR)/man8/stud.8 + rmdir $(DESTDIR)$(MANDIR)/man8 --ignore-fail-on-non-empty + + rm -f $(UPSTARTDIR)/stud.conf + + rm -f $(CONFDIR)/$(CONFFILE) + rmdir $(CONFDIR) --ignore-fail-on-non-empty + clean: rm -f stud $(OBJS) diff --git a/configuration.c b/configuration.c index 25fead7..ddf7ab2 100644 --- a/configuration.c +++ b/configuration.c @@ -45,6 +45,7 @@ #define CFG_DAEMON "daemon" #define CFG_WRITE_IP "write-ip" #define CFG_WRITE_PROXY "write-proxy" +#define CFG_WRITE_XFF "write-xff" #define CFG_PEM_FILE "pem-file" #define CFG_PROXY_PROXY "proxy-proxy" @@ -115,6 +116,7 @@ stud_config * config_new (void) { r->PMODE = SSL_SERVER; r->WRITE_IP_OCTET = 0; r->WRITE_PROXY_LINE = 0; + r->WRITE_XFF_LINE = 0; r->PROXY_PROXY_LINE = 0; r->CHROOT = NULL; r->UID = 0; @@ -684,6 +686,9 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int else if (strcmp(k, CFG_WRITE_PROXY) == 0) { r = config_param_val_bool(v, &cfg->WRITE_PROXY_LINE); } + else if (strcmp(k, CFG_WRITE_XFF) == 0) { + r = config_param_val_bool(v, &cfg->WRITE_XFF_LINE); + } else if (strcmp(k, CFG_PROXY_PROXY) == 0) { r = config_param_val_bool(v, &cfg->PROXY_PROXY_LINE); } @@ -922,6 +927,8 @@ void config_print_usage_fd (char *prog, stud_config *cfg, FILE *out) { fprintf(out, " --write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line\n" ); fprintf(out, " before actual data\n"); fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_PROXY_LINE)); + fprintf(out, " --write-xff Write X-Forwarded-For header before actual data\n" ); + fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_XFF_LINE)); fprintf(out, " --proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line\n" ); fprintf(out, " before actual data\n"); fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->PROXY_PROXY_LINE)); @@ -1109,6 +1116,12 @@ void config_print_default (FILE *fd, stud_config *cfg) { fprintf(fd, FMT_STR, CFG_WRITE_PROXY, config_disp_bool(cfg->WRITE_PROXY_LINE)); fprintf(fd, "\n"); + fprintf(fd, "# Report client address using X-Forwarded-For header.\n"); + fprintf(fd, "#\n"); + fprintf(fd, "# type: boolean\n"); + fprintf(fd, FMT_STR, CFG_WRITE_XFF, config_disp_bool(cfg->WRITE_XFF_LINE)); + fprintf(fd, "\n"); + fprintf(fd, "# Proxy an existing SENDPROXY protocol header through this request.\n"); fprintf(fd, "#\n"); fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_WRITE_PROXY); @@ -1163,6 +1176,7 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) { { CFG_DAEMON, 0, &cfg->DAEMONIZE, 1 }, { CFG_WRITE_IP, 0, &cfg->WRITE_IP_OCTET, 1 }, { CFG_WRITE_PROXY, 0, &cfg->WRITE_PROXY_LINE, 1 }, + { CFG_WRITE_XFF, 0, &cfg->WRITE_XFF_LINE, 1 }, { CFG_PROXY_PROXY, 0, &cfg->PROXY_PROXY_LINE, 1 }, { "test", 0, NULL, 't' }, diff --git a/configuration.h b/configuration.h index 3ea68b0..04f339f 100644 --- a/configuration.h +++ b/configuration.h @@ -42,6 +42,7 @@ struct __stud_config { PROXY_MODE PMODE; int WRITE_IP_OCTET; int WRITE_PROXY_LINE; + int WRITE_XFF_LINE; int PROXY_PROXY_LINE; char *CHROOT; uid_t UID; diff --git a/stud.8 b/stud.8 index bca288a..2d96afd 100644 --- a/stud.8 +++ b/stud.8 @@ -46,6 +46,7 @@ .Op Fl qs .Op Fl -write-ip .Op Fl -write-proxy +.Op Fl -write-xff .Ar certificate.pem .Sh DESCRIPTION .Nm @@ -117,6 +118,8 @@ data. .It Fl -write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line before actual data. +.It Fl -write-xff +Write X-Forwarded-For header before actual data. .El .Sh SEE ALSO .Xr ciphers 1SSL , diff --git a/stud.c b/stud.c index 1e83617..937936b 100644 --- a/stud.c +++ b/stud.c @@ -168,6 +168,8 @@ typedef struct proxystate { int clear_connected:1; /* Clear stream is connected */ int renegotiation:1; /* Renegotation is occuring */ + int sent_xff:1; /* Have sent X-Forwarded-For header */ + SSL *ssl; /* OpenSSL SSL state */ struct sockaddr_storage remote_ip; /* Remote ip returned from `accept` */ @@ -803,6 +805,37 @@ static void prepare_proxy_line(struct sockaddr* ai_addr) { } } +char *prepare_xff_line(struct sockaddr* ai_addr) { + static char xff_line[128]; + xff_line[0] = 0; + char tcp6_address_string[INET6_ADDRSTRLEN]; + + if (ai_addr->sa_family == AF_INET) { + struct sockaddr_in* addr = (struct sockaddr_in*)ai_addr; + size_t res = snprintf(xff_line, + sizeof(xff_line), + "X-Forwarded-For: %s\r\n", + inet_ntoa(addr->sin_addr)); + assert(res < sizeof(xff_line)); + return xff_line; + } + else if (ai_addr->sa_family == AF_INET6 ) { + struct sockaddr_in6* addr = (struct sockaddr_in6*)ai_addr; + inet_ntop(AF_INET6,&(addr->sin6_addr),tcp6_address_string,INET6_ADDRSTRLEN); + size_t res = snprintf(xff_line, + sizeof(xff_line), + "X-Forwarded-For:%s\r\n", + tcp6_address_string); + assert(res < sizeof(xff_line)); + return xff_line; + } + else { + ERR("The --write-xff mode is not implemented for this address family.\n"); + exit(1); + } + return NULL; +} + /* Create the bound socket in the parent process */ static int create_main_socket() { struct addrinfo *ai, hints; @@ -973,7 +1006,50 @@ static void clear_write(struct ev_loop *loop, ev_io *w, int revents) { assert(!ringbuffer_is_empty(&ps->ring_ssl2clear)); char *next = ringbuffer_read_next(&ps->ring_ssl2clear, &sz); - t = send(fd, next, sz, MSG_NOSIGNAL); + + + if (ps->sent_xff == 0 && CONFIG->WRITE_XFF_LINE) { + for(int i = 0; i < sz; i++) { + if(next[i] == '\n') { + const char *xff_line = prepare_xff_line((struct sockaddr *)&(ps->remote_ip)); + /* Send the request through because we don't know where it's + * from */ + if(xff_line == NULL) { + ps->sent_xff = 1; + break; + } + int temporary_buffer_length = sz + strlen(xff_line); + char *temporary_buffer = (char *)malloc(temporary_buffer_length); + /* Send the request through because we don't have enough + * RAM to fiddle with the headers */ + if(temporary_buffer == NULL) { + ps->sent_xff = 1; + break; + } + /* Create a temporary buffer with the original HTTP line, the + * XFF header, and the remainer of the ringbuffer */ + int http_line_length = i+1; + int xff_line_length = strlen(xff_line); + memcpy(temporary_buffer, next, http_line_length); + memcpy(temporary_buffer+http_line_length, xff_line, xff_line_length); + memcpy(temporary_buffer+http_line_length+xff_line_length, next+http_line_length, sz-http_line_length); + + ringbuffer_read_pop(&ps->ring_ssl2clear); + + char *write_ptr = ringbuffer_write_ptr(&ps->ring_ssl2clear); + memcpy(write_ptr, temporary_buffer, temporary_buffer_length); + ringbuffer_write_append(&ps->ring_ssl2clear, temporary_buffer_length); + next = write_ptr; + sz = temporary_buffer_length; + free(temporary_buffer); + + ps->sent_xff = 1; + break; + } + } + } + + t = send(fd, next, sz, MSG_NOSIGNAL); if (t > 0) { if (t == sz) { @@ -1361,6 +1437,7 @@ static void handle_accept(struct ev_loop *loop, ev_io *w, int revents) { ps->clear_connected = 0; ps->handshaked = 0; ps->renegotiation = 0; + ps->sent_xff = 0; ps->remote_ip = addr; ringbuffer_init(&ps->ring_clear2ssl); ringbuffer_init(&ps->ring_ssl2clear); diff --git a/upstart/stud.conf b/upstart/stud.conf new file mode 100644 index 0000000..d38e14f --- /dev/null +++ b/upstart/stud.conf @@ -0,0 +1,16 @@ +description "Scalable TLS Unwrapping Daemon (stud)" +author "Bump Technologies (upstart script contributed by axsh Co.)" + +stop on runlevel [S016] +start on runlevel [23] + +respawn +respawn limit unlimited + +script + # If this job is failed, upstart supervisor will run again soon. It's too fast. + # Therefore a proper sleep is needed. + sleep ${SLEEP_SEC:-1} + + exec stud --config /etc/stud/stud.cfg +end script