diff --git a/Makefile b/Makefile index 33aca4b..ca51a6c 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,49 @@ # Configuration +VERSION="v1.6i" USELIBWRAP=1 # Use libwrap? +PREFIX=/usr/local +MAN=sslh.8.gz # man page name # End of configuration -- the rest should take care of # itself CC = gcc +CFLAGS=-Wall #LIBS=-lnet LIBS= ifneq ($(strip $(USELIBWRAP)),) LIBS:=$(LIBS) -lwrap - CFLAGS=-DLIBWRAP + CFLAGS:=$(CFLAGS) -DLIBWRAP endif -all: - $(CC) $(CFLAGS) -o sslh sslh.c $(LIBS) +all: sslh $(MAN) + +sslh: sslh.c Makefile + $(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh sslh.c $(LIBS) strip sslh +$(MAN): sslh.pod Makefile + pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN) +# generic install: install binary and man page +install: sslh $(MAN) + install -D sslh $(PREFIX)/sbin/sslh + install -D -m 0644 $(MAN) $(PREFIX)/share/man/man8/$(MAN) + +# "extended" install for Debian: install startup script +install-debian: install sslh $(MAN) + sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh + chmod 755 /etc/init.d/sslh + cp scripts/etc.default.sslh /etc/default/sslh + update-rc.d sslh defaults + +uninstall: + rm -f $(PREFIX)/sbin/sslh $(PREFIX)/share/man/man8/$(MAN) /etc/init.d/sslh /etc/default/sslh + update-rc.d sslh remove + +clean: + rm -f sslh $(MAN) diff --git a/README b/README index 9954f86..fcf60e1 100644 --- a/README +++ b/README @@ -1,10 +1,20 @@ -sslh -- A ssl/ssh multiplexer. +===== sslh -- A ssl/ssh multiplexer. ===== sslh lets one accept both HTTPS and SSH connections on the same port. It makes it possible to connect to an SSH server on port 443 (e.g. from inside a corporate firewall) while still serving HTTPS on that port. +==== Compile and install ==== + +If you're lucky, the Makefile will work for you: + +make install + +(see below for configuration hints) + + +Otherwise: Compilation instructions: @@ -29,18 +39,29 @@ cp sslh /usr/local/sbin cp scripts/etc.init.d.sslh /etc/init.d/sslh cp scripts/etc.default.sslh /etc/default/sslh +and probably create links in /etc/rc.d so that the server +start automatically at boot-up, e.g. under Debian: +update-rc.d sslh defaults + + + +==== Configuration ==== + You can edit settings in /etc/default/sslh: -PIDFILE=/var/run/sslh.pid LISTEN=ifname:443 SSH=localhost:22 SSL=localhost:443 A good scheme is to use the external name of the machine in -$LISTEN, and bind httpd to localhost:443: that way, https -connections coming from inside your network don't need to go -through sslh, and sslh is only there as a frontal for -connections coming from the internet. +$LISTEN, and bind httpd to localhost:443 (instead of all +binding to all interfaces): that way, https connections +coming from inside your network don't need to go through +sslh, and sslh is only there as a frontal for connections +coming from the internet. + + +==== Libwrap support ==== Sslh can optionnaly perform libwrap checks for the sshd service: because the connection to sshd will be coming @@ -51,6 +72,17 @@ Comments? questions? sslh@rutschle.net HISTORY +v1.6: 25APR2009 + Added -V, version option. + Install target directory configurable in Makefile + Changed syslog prefix in auth.log to "sslh[%pid]" + Man page + new 'make install' and 'make install-debian' targets + PID file now specified using -P command line option + Actually fixed zombie generation (the v1.5 patch got + lost, doh!) + + v1.5: 10DEC2008 Fixed zombie generation. Added support scripts (), Makefile. diff --git a/scripts/etc.default.sslh b/scripts/etc.default.sslh index 4fb2880..23339aa 100755 --- a/scripts/etc.default.sslh +++ b/scripts/etc.default.sslh @@ -1,4 +1,3 @@ -PIDFILE=/var/run/sslh.pid LISTEN=ifname:443 SSH=localhost:22 SSL=localhost:443 diff --git a/scripts/etc.init.d.sslh b/scripts/etc.init.d.sslh index f08782f..27b8694 100755 --- a/scripts/etc.init.d.sslh +++ b/scripts/etc.init.d.sslh @@ -14,17 +14,18 @@ facility=user.info # /etc/init.d/sslh: start and stop the sslh proxy daemon # Defaults -- can be overridden in /etc/default/sslh -PIDFILE=/var/run/sslh.pid LISTEN=thelonious:443 SSH=localhost:22 SSL=localhost:443 if test -f /etc/default/sslh; then . /etc/default/sslh - export PIDFILE=${PIDFILE} fi -DAEMON=/usr/local/sbin/sslh +# The prefix is normally filled by make install. If +# installing by hand, fill it in yourself! +PREFIX= +DAEMON=$PREFIX/sbin/sslh start() { diff --git a/sslh.c b/sslh.c index b0c6674..a62ae15 100644 --- a/sslh.c +++ b/sslh.c @@ -20,8 +20,7 @@ */ -#define VERSION "1.5" - +#define _GNU_SOURCE #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #ifdef LIBWRAP #include @@ -50,15 +50,16 @@ if (res == -1) { \ } #define USAGE_STRING \ -"sslh v" VERSION "\n" \ +"sslh " VERSION "\n" \ "usage:\n" \ -"\texport PIDFILE=/var/run/sslhc.pid\n" \ "\tsslh [-t ] -u -p [listenaddr:] \n" \ -"\t\t-s [sshhost:]port -l [sslhost:]port [-v]\n\n" \ +"\t\t-s [sshhost:]port -l [sslhost:]port [-P pidfile] [-v] [-V]\n\n" \ "-v: verbose\n" \ +"-V: version\n" \ "-p: address and port to listen on. default: 0.0.0.0:443\n" \ "-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \ "-l: SSL address: where to connect an SSL connection.\n" \ +"-P: PID file. Default: /var/run/sslh.pid\n" \ "" int verbose = 0; /* That's really quite global */ @@ -188,7 +189,7 @@ void resolve_name(struct sockaddr *sock, char* fullname) { res = getaddrinfo(host, serv, &hint, &addr); if (res) { - fprintf(stderr, "%s\n", gai_strerror(res)); + fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname); if (res == EAI_SERVICE) fprintf(stderr, "(Check you have specified all ports)\n"); exit(1); @@ -306,25 +307,31 @@ void start_shoveler(int in_socket) exit(0); } -/* SIGCHLD handling: - * we need to reap our children - */ -void child_handler(int signo) -{ - signal(SIGCHLD, &child_handler); - wait(NULL); -} void setup_signals(void) { - void* res; + int res; + struct sigaction action; - res = signal(SIGCHLD, &child_handler); - if (res == SIG_ERR) { - perror("signal"); - exit(1); - } + /* Request no SIGCHLD is sent upon termination of + * the children */ + memset(&action, 0, sizeof(action)); + action.sa_handler = NULL; + action.sa_flags = SA_NOCLDWAIT; + res = sigaction(SIGCHLD, &action, NULL); + CHECK_RES_DIE(res, "sigaction"); } +/* Open syslog connection with appropriate banner; + * banner is made up of basename(bin_name)+"[pid]" */ +void setup_syslog(char* bin_name) { + char *name1, *name2; + + name1 = strdup(bin_name); + asprintf(&name2, "%s[%d]", basename(name1), getpid()); + openlog(name2, LOG_CONS, LOG_AUTH); + free(name1); + /* Don't free name2, as openlog(3) uses it (at least in glibc) */ +} /* We don't want to run as root -- drop priviledges if required */ void drop_privileges(char* user_name) @@ -345,14 +352,10 @@ void drop_privileges(char* user_name) } /* Writes my PID if $PIDFILE is defined */ -void write_pid_file(void) +void write_pid_file(char* pidfile) { - char *pidfile = getenv("PIDFILE"); FILE *f; - if (!pidfile) - return; - f = fopen(pidfile, "w"); if (!f) { perror(pidfile); @@ -391,12 +394,13 @@ int main(int argc, char *argv[]) char listen_str[] = "0.0.0.0:443"; char ssl_str[] = "localhost:442"; char ssh_str[] = "localhost:22"; + char *pid_file = "/var/run/sslh.pid"; resolve_name(&addr_listen, listen_str); resolve_name(&addr_ssl, ssl_str); resolve_name(&addr_ssh, ssh_str); - while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) { + while ((c = getopt(argc, argv, "t:l:s:p:P:vVu:")) != EOF) { switch (c) { case 't': @@ -419,10 +423,18 @@ int main(int argc, char *argv[]) verbose += 1; break; + case 'V': + printf("sslh %s\n", VERSION); + exit(0); + case 'u': user_name = optarg; break; + case 'P': + pid_file = optarg; + break; + default: fprintf(stderr, USAGE_STRING); exit(2); @@ -438,7 +450,7 @@ int main(int argc, char *argv[]) if (fork() > 0) exit(0); /* Detach */ - write_pid_file(); + write_pid_file(pid_file); drop_privileges(user_name); @@ -447,7 +459,7 @@ int main(int argc, char *argv[]) CHECK_RES_DIE(res, "setsid: already process leader"); /* Open syslog connection */ - openlog(argv[0], LOG_CONS, LOG_AUTH); + setup_syslog(argv[0]); /* Main server loop: accept connections, find what they are, fork shovelers */ while (1) diff --git a/sslh.pod b/sslh.pod new file mode 100644 index 0000000..b428c37 --- /dev/null +++ b/sslh.pod @@ -0,0 +1,128 @@ +# I'm just not gonna write troff :-) + +=head1 NAME + + sslh - ssl/ssh multiplexer + +=head1 SYNOPSIS + +sslh [ B<-t> I ] [B<-p> I] [B<-l> I] [B<-s> I] [B<-u> I] [B<-P> I] [-v] [-V] + +=head1 DESCRIPTION + +B lets one accept both HTTPS and SSH connections on +the same port. It makes it possible to connect to an SSH +server on port 443 (e.g. from inside a corporate firewall, +which almost never block port 443) while still serving HTTPS +on that port. + +The idea is to have B listen to the external 443 port, +accept the incoming connections, work out what type of +connection it is, and then fordward to the appropriate +server. + +=head2 Protocol detection + +The protocol detection is made based on a small difference +between SSL and SSH: an SSL client connecting to a server +speaks first, whereas an SSH client expects the SSH server +to speak first (announcing itself with a banner). B +waits for some time for the incoming connection to send data. +If it does before the timeout occurs, it is supposed to be +an SSL connection. Otherwise, it is supposed to be an SSH +connection. + +=head2 Libwrap support + +One drawback of B is that the B and B +servers do not see the original IP address of the client +anymore, as the connection is forwarded through B. +B provides enough logging to circumvent that problem. +However it is common to limit access to B using +B or B. For this reason, B can be +compiled to check SSH accesses against SSH access lists as +defined in F and F. + +=head1 OPTIONS + +=over 4 + +=item B<-t> I + +Timeout before a connection is considered to be SSH. Default +is 2s. + +=item B<-p> I + +Interface and port on which to listen, e.g. I, +where I is the name of an interface (typically the +IP address on which the Internet connection ends up). + +Defaults to I<0.0.0.0:443> (listen to port 443 on all +available interfaces). + +=item B<-l> I + +Interface and port on which to forward SSL connection, +typically I. + +Defaults to I (this assumes you would +configure your B process to listen to port 443). + +Note that you can set B to listen on I and +B to listen on I: this allows clients +inside your network to just connect directly to B. + +=item B<-s> I + +Interface and port on which to forward SSH connection, +defaults to I. + +=item B<-v> + +Increase verboseness. + +=item B<-V> + +Prints B version. + +=item B<-u> I + +Requires to run under the specified username. Defaults to +I (which is not perfect -- ideally B should +run under its own UID). + +=item B<-P> I + +Specifies the file in which to write the PID of the main +server. Defaults to I. + +=back + +=head1 FILES + +=over 4 + +=item F + +Start-up script. The standard actions B, B and +B are supported. + +=item F + +Server configuration. These are environement variables +loaded by the start-up script and passed to B as +command-line arguments. Refer to the OPTIONS section for a +detailed explanation of the variables used by B. + +=back + +=head1 SEE ALSO + +Last version available from +L, and can be tracked +from L. + +=head1 AUTHOR + +Written by Yves Rutschle