Compare commits

..
2 Commits
Author SHA1 Message Date
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
5 changed files with 261 additions and 52 deletions

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
# Configuration
USELIBWRAP=1 # Use libwrap?
# End of configuration -- the rest should take care of
# itself
CC = gcc
#LIBS=-lnet
LIBS=
ifneq ($(strip $(USELIBWRAP)),)
LIBS:=$(LIBS) -lwrap
CFLAGS=-DLIBWRAP
endif
all:
$(CC) $(CFLAGS) -o sslh sslh.c $(LIBS)
strip sslh
+83
View File
@@ -0,0 +1,83 @@
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.
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
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.
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.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.
+4
View File
@@ -0,0 +1,4 @@
PIDFILE=/var/run/sslh.pid
LISTEN=ifname:443
SSH=localhost:22
SSL=localhost:443
+62
View File
@@ -0,0 +1,62 @@
#! /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
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
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
+89 -52
View File
@@ -18,35 +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.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.
*/
#define VERSION "1.2"
#define VERSION "1.5"
#include <sys/types.h>
#include <fcntl.h>
@@ -61,6 +35,13 @@ v1.0:
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#ifdef LIBWRAP
#include <tcpd.h>
int allow_severity =0, deny_severity = 0;
#endif
#define CHECK_RES_DIE(res, str) \
if (res == -1) { \
@@ -72,16 +53,22 @@ if (res == -1) { \
"sslh v" VERSION "\n" \
"usage:\n" \
"\texport PIDFILE=/var/run/sslhc.pid\n" \
"\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
"\t\t-s [sshhost:]port -l [sslhost:]port [-v]\n\n" \
"-v: verbose\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" \
""
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);
@@ -91,10 +78,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);
@@ -178,10 +162,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;
@@ -191,7 +173,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 {
@@ -215,15 +197,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
*/
@@ -234,6 +262,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);
@@ -247,15 +276,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));
@@ -342,7 +374,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[])
@@ -356,11 +388,13 @@ 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";
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) {
switch (c) {
@@ -370,15 +404,15 @@ 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':
@@ -400,7 +434,7 @@ 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 */
@@ -412,6 +446,9 @@ int main(int argc, char *argv[])
res = setsid();
CHECK_RES_DIE(res, "setsid: already process leader");
/* Open syslog connection */
openlog(argv[0], LOG_CONS, LOG_AUTH);
/* Main server loop: accept connections, find what they are, fork shovelers */
while (1)
{