Compare commits

..
4 Commits
Author SHA1 Message Date
Yves Rutschle 0658982705 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!)
2013-07-10 23:10:43 +02:00
Yves Rutschle b965d735b8 v1.5: 10DEC2008
Fixed zombie generation.
        Added support scripts (), Makefile.
        Changed all 'connexions' to 'connections' to please
        pesky users. Damn users.
2013-07-10 23:09:40 +02:00
Yves Rutschle 3386a64a4d v1.3 2013-07-10 23:07:49 +02:00
Yves Rutschle b49617923f v1.2 2013-07-10 23:07:20 +02:00
6 changed files with 486 additions and 71 deletions

No files matched your search

+49
View File
@@ -0,0 +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:=$(CFLAGS) -DLIBWRAP
endif
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)
+115
View File
@@ -0,0 +1,115 @@
===== 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:
Solaris:
cc -o sslh sslh.c -lresolv -lsocket -lnsl
LynxOS:
gcc -o tcproxy tcproxy.c -lnetinet
Linux:
cc -o sslh sslh.c -lnet
or:
cc -o sslh sslh.c
To compile with libwrap support:
cc -o sslh -DLIBWRAP sslh.c -lwrap
To install:
make
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<x>.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:
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 (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
locally from sslh, sshd cannot determine the IP of the
client.
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.
Changed all 'connexions' to 'connections' to please
pesky users. Damn users.
v1.4: 13JUL2008
Added libwrap support for ssh service (Christian Weinberger)
Only SSH is libwraped, not SSL.
v1.3: 14MAY2008
Added parsing for local interface to listen on
Changed default SSL connection to port 442 (443 doesn't make
sense as a default as we're already listening on 443)
Syslog incoming connections
v1.2: 12MAY2008
Fixed compilation warning for AMD64 (Thx Daniel Lange)
v1.1: 21MAY2007
Making sslhc more like a real daemon:
* If $PIDFILE is defined, write first PID to it upon startup
* Fork at startup (detach from terminal)
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
* Less memory usage (?)
v1.0:
* Basic functionality: privilege dropping, target hostnames and ports
configurable.
+3
View File
@@ -0,0 +1,3 @@
LISTEN=ifname:443
SSH=localhost:22
SSL=localhost:443
+63
View File
@@ -0,0 +1,63 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: sslh
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: sslh proxy ssl & ssh connections
### END INIT INFO
set -e
tag=sslh
facility=user.info
# /etc/init.d/sslh: start and stop the sslh proxy daemon
# Defaults -- can be overridden in /etc/default/sslh
LISTEN=thelonious:443
SSH=localhost:22
SSL=localhost:443
if test -f /etc/default/sslh; then
. /etc/default/sslh
fi
# The prefix is normally filled by make install. If
# installing by hand, fill it in yourself!
PREFIX=
DAEMON=$PREFIX/sbin/sslh
start()
{
echo "Start services: sslh"
$DAEMON -u nobody -p ${LISTEN} -s ${SSH} -l ${SSL}
logger -t ${tag} -p ${facility} -i 'Started sslh'
}
stop()
{
echo "Stop services: sslh"
killall $DAEMON
rm ${PIDFILE}
logger -t ${tag} -p ${facility} -i 'Stopped sslh'
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 5
start
;;
*)
echo "Usage: /etc/init.d/sslh {start|stop|restart}" >&2
;;
esac
exit 0
+128 -71
View File
@@ -1,7 +1,7 @@
/*
Reimplementation of sslh in C
# Copyright (C) 2007 Yves Rutschle
# Copyright (C) 2007-2008 Yves Rutschle
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
@@ -18,33 +18,9 @@
# The full text for the General Public License is here:
# http://www.gnu.org/licenses/gpl.html
Comments? questions? sslh@rutschle.net
Compilation instructions:
Solaris:
cc -o sslh sslh.c -lresolv -lsocket -lnsl
LynxOS:
gcc -o tcproxy tcproxy.c -lnetinet
HISTORY
v1.1: 21MAY2007
Making sslhc more like a real daemon:
* If $PIDFILE is defined, write first PID to it upon startup
* Fork at startup (detach from terminal)
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
* Less memory usage (?)
v1.0:
* Basic functionality: privilege dropping, target hostnames and ports
configurable.
*/
#define VERSION "1.1"
#define _GNU_SOURCE
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
@@ -58,6 +34,14 @@ v1.0:
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#include <libgen.h>
#ifdef LIBWRAP
#include <tcpd.h>
int allow_severity =0, deny_severity = 0;
#endif
#define CHECK_RES_DIE(res, str) \
if (res == -1) { \
@@ -65,18 +49,27 @@ if (res == -1) { \
exit(1); \
}
#define USAGE_STRING "usage:\n" \
"\texport PIDFILE=/var/run/sslhc.pid" \
"\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
#define USAGE_STRING \
"sslh " VERSION "\n" \
"usage:\n" \
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \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 */
/* Starts a listening socket on specified port.
/* Starts a listening socket on specified address.
Returns file descriptor
*/
int start_listen_socket(int port)
int start_listen_socket(struct sockaddr *addr)
{
struct sockaddr_in saddr;
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
int sockfd, res, reuse;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -86,10 +79,7 @@ int start_listen_socket(int port)
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
CHECK_RES_DIE(res, "setsockopt");
memset(&saddr, 0, sizeof(saddr));
saddr.sin_port = htons(port);
res = bind (sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
CHECK_RES_DIE(res, "bind");
res = listen (sockfd, 5);
@@ -173,10 +163,8 @@ char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
/* turns a "hostname:port" string into a struct sockaddr;
sock: socket address to which to copy the addr
fullname: input string -- it gets clobbered
serv: default service/port
(defaults don't work yet)
*/
void resolve_name(struct sockaddr *sock, char* fullname, int port) {
void resolve_name(struct sockaddr *sock, char* fullname) {
struct addrinfo *addr, hint;
char *serv, *host;
int res;
@@ -186,7 +174,7 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
if (!sep) /* No separator: parameter is just a port */
{
serv = fullname;
fprintf(stderr, "names must be fully specified as hostname:port for the moment\n");
fprintf(stderr, "names must be fully specified as hostname:port\n");
exit(1);
}
else {
@@ -201,7 +189,7 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
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);
@@ -210,15 +198,61 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
memcpy(sock, addr->ai_addr, sizeof(*sock));
}
/* syslogs who connected to where */
void log_connection(int socket, char* target)
{
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char buf[64];
int res;
res = getpeername(socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
syslog(LOG_INFO, "connection from %s forwarded to %s\n",
sprintaddr(buf, sizeof(buf), &peeraddr), target);
}
/*
* Settings that depend on the command line. That's less global than verbose * :-)
* They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
* to pass it all as parameters
*/
int timeout = 2;
int listen_port = 443;
struct sockaddr addr_listen;
struct sockaddr addr_ssl, addr_ssh;
/* libwrap (tcpd): check the ssh connection is legal. This is necessary because
* the actual sshd will only see a connection coming from localhost and can't
* apply the rules itself.
*/
void check_access_rights(int in_socket)
{
#ifdef LIBWRAP
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char addr_str[1024];
struct hostent *host;
struct in_addr addr;
int res;
res = getpeername(in_socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
inet_ntop(AF_INET, &((struct sockaddr_in*)&peeraddr)->sin_addr, addr_str, sizeof(addr_str));
addr.s_addr = inet_addr(addr_str);
host = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
if (!hosts_ctl("sshd", (host ? host->h_name : STRING_UNKNOWN), addr_str, STRING_UNKNOWN)) {
if (verbose)
fprintf(stderr, "access denied\n");
log_connection(in_socket, "access denied");
close(in_socket);
exit(0);
}
#endif
}
/* Child process that finds out what to connect to and proxies
*/
@@ -229,6 +263,7 @@ void start_shoveler(int in_socket)
struct sockaddr *saddr;
int res;
int out_socket;
char *target;
FD_ZERO(&fds);
FD_SET(in_socket, &fds);
@@ -242,15 +277,18 @@ void start_shoveler(int in_socket)
if (FD_ISSET(in_socket, &fds)) {
/* The client wrote something to the socket: it's an SSL connection */
saddr = &addr_ssl;
if (verbose)
fprintf(stderr, "Forwarding to SSL\n");
target = "SSL";
} else {
/* The client hasn't written anything and we timed out: connect to SSH */
saddr = &addr_ssh;
if (verbose)
fprintf(stderr, "Forwarding to SSH\n");
target = "SSH";
/* do hosts_access check if built with libwrap support */
check_access_rights(in_socket);
}
log_connection(in_socket, target);
/* Connect the target socket */
out_socket = socket(AF_INET, SOCK_STREAM, 0);
res = connect(out_socket, saddr, sizeof(addr_ssl));
@@ -269,22 +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)
{
int res;
struct sigaction action;
res = (int)signal(SIGCHLD, &child_handler);
CHECK_RES_DIE(res, "signal");
/* 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)
@@ -305,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);
@@ -334,7 +377,7 @@ void printsettings(void)
timeout
);
fprintf(stderr, "SSH addr: %s\n", sprintaddr(buf, sizeof(buf), &addr_ssh));
fprintf(stderr, "listening on port %d\n", listen_port);
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
}
int main(int argc, char *argv[])
@@ -348,13 +391,16 @@ int main(int argc, char *argv[])
/* Init defaults */
char *user_name = "nobody";
char ssl_str[] = "localhost:443";
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_ssl, ssl_str, 443);
resolve_name(&addr_ssh, ssh_str, 22);
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':
@@ -362,25 +408,33 @@ int main(int argc, char *argv[])
break;
case 'p':
listen_port = atoi(optarg);
resolve_name(&addr_listen, optarg);
break;
case 'l':
resolve_name(&addr_ssl, optarg, 443);
resolve_name(&addr_ssl, optarg);
break;
case 's':
resolve_name(&addr_ssh, optarg, 22);
resolve_name(&addr_ssh, optarg);
break;
case 'v':
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);
@@ -392,11 +446,11 @@ int main(int argc, char *argv[])
setup_signals();
listen_socket = start_listen_socket(listen_port);
listen_socket = start_listen_socket(&addr_listen);
if (fork() > 0) exit(0); /* Detach */
write_pid_file();
write_pid_file(pid_file);
drop_privileges(user_name);
@@ -404,6 +458,9 @@ int main(int argc, char *argv[])
res = setsid();
CHECK_RES_DIE(res, "setsid: already process leader");
/* Open syslog connection */
setup_syslog(argv[0]);
/* Main server loop: accept connections, find what they are, fork shovelers */
while (1)
{
+128
View File
@@ -0,0 +1,128 @@
# I'm just not gonna write troff :-)
=head1 NAME
sslh - ssl/ssh multiplexer
=head1 SYNOPSIS
sslh [ B<-t> I<num> ] [B<-p> I<listening address>] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-V]
=head1 DESCRIPTION
B<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,
which almost never block port 443) while still serving HTTPS
on that port.
The idea is to have B<sslh> 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<sslh>
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<sslh> is that the B<ssh> and B<httpd>
servers do not see the original IP address of the client
anymore, as the connection is forwarded through B<sslh>.
B<sslh> provides enough logging to circumvent that problem.
However it is common to limit access to B<ssh> using
B<libwrap> or B<tcpd>. For this reason, B<sslh> can be
compiled to check SSH accesses against SSH access lists as
defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
=head1 OPTIONS
=over 4
=item B<-t> I<num>
Timeout before a connection is considered to be SSH. Default
is 2s.
=item B<-p> I<listening address>
Interface and port on which to listen, e.g. I<foobar:443>,
where I<foobar> 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<target address for SSL>
Interface and port on which to forward SSL connection,
typically I<localhost:443>.
Defaults to I<localhost:442> (this assumes you would
configure your B<httpd> process to listen to port 443).
Note that you can set B<sslh> to listen on I<ext_ip:443> and
B<httpd> to listen on I<localhost:443>: this allows clients
inside your network to just connect directly to B<httpd>.
=item B<-s> I<target address for SSH>
Interface and port on which to forward SSH connection,
defaults to I<localhost:22>.
=item B<-v>
Increase verboseness.
=item B<-V>
Prints B<sslh> version.
=item B<-u> I<username>
Requires to run under the specified username. Defaults to
I<nobody> (which is not perfect -- ideally B<sslh> should
run under its own UID).
=item B<-P> I<pidfile>
Specifies the file in which to write the PID of the main
server. Defaults to I</var/run/sslh.pid>.
=back
=head1 FILES
=over 4
=item F</etc/init.d/sslh>
Start-up script. The standard actions B<start>, B<stop> and
B<restart> are supported.
=item F</etc/default/sslh>
Server configuration. These are environement variables
loaded by the start-up script and passed to B<sslh> as
command-line arguments. Refer to the OPTIONS section for a
detailed explanation of the variables used by B<sslh>.
=back
=head1 SEE ALSO
Last version available from
L<http://www.rutschle.net/tech/sslh>, and can be tracked
from L<http://freshmeat.net/projects/sslh/>.
=head1 AUTHOR
Written by Yves Rutschle