mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-25 01:06:20 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9c9941988 |
No files matched your search
@@ -1,4 +1,31 @@
|
||||
v1.8:
|
||||
v1.9: 02AUG2011
|
||||
WARNING: Options changed, you'll need to update your
|
||||
start-up scripts! Log format changed, you'll need to
|
||||
update log processing scripts!
|
||||
|
||||
Now supports IPv6 throughout (both on listening and
|
||||
forwarding)
|
||||
|
||||
Logs now contain IPv6 addresses, local forwarding
|
||||
address, and resolves names (unless --numeric is
|
||||
specified).
|
||||
|
||||
Introduced long options.
|
||||
|
||||
Options -l, -s and -o replaced by their long
|
||||
counterparts.
|
||||
|
||||
Defaults for SSL and SSH options suppressed (it's
|
||||
legitimate to want to use sslh to mux OpenVPN and
|
||||
tinc while not caring about SSH nor SSL).
|
||||
|
||||
Bind to multiple addresses with multiple -p options.
|
||||
|
||||
Support for tinc VPN (experimental).
|
||||
|
||||
Numeric logging option.
|
||||
|
||||
v1.8: 15JUL2011
|
||||
Changed log format to make it possible to link
|
||||
connections to subsequent logs from other services.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configuration
|
||||
|
||||
VERSION="v1.8"
|
||||
VERSION="v1.9"
|
||||
USELIBWRAP= # Use libwrap?
|
||||
PREFIX=/usr/local
|
||||
|
||||
@@ -29,11 +29,11 @@ all: sslh $(MAN)
|
||||
|
||||
sslh: $(OBJS) sslh-fork sslh-select
|
||||
|
||||
sslh-fork: $(OBJS) sslh-fork.o Makefile
|
||||
sslh-fork: $(OBJS) sslh-fork.o Makefile common.h
|
||||
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh-fork sslh-fork.o $(OBJS) $(LIBS)
|
||||
strip sslh-fork
|
||||
|
||||
sslh-select: $(OBJS) sslh-select.o Makefile
|
||||
sslh-select: $(OBJS) sslh-select.o Makefile common.h
|
||||
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh-select sslh-select.o $(OBJS) $(LIBS)
|
||||
strip sslh-select
|
||||
|
||||
|
||||
@@ -104,22 +104,22 @@ In that case, you end up with something like this:
|
||||
|
||||
ssh -> proxytunnel -e --------ssh/ssl------> stunnel ---ssh---> sslh --> sshd
|
||||
|
||||
navigateur --------http/ssl------> stunnel ---http---> sslh --> http:80
|
||||
Web browser --------http/ssl------> stunnel ---http---> sslh --> http:80
|
||||
|
||||
Configuration goes like this:
|
||||
|
||||
On the server side, using stunnel3:
|
||||
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i -l localhost:80 -s localhost:22
|
||||
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i --ssl localhost:80 --ssh localhost:22
|
||||
|
||||
stunnel options: -f for foreground/debugging, -p specifies
|
||||
the key + certificate, -d specifies which interface and port
|
||||
we're listening to for incoming connexions, -l summons sslh
|
||||
in inetd mode.
|
||||
|
||||
sslh options: -i for inetd mode, -l to forward SSL
|
||||
sslh options: -i for inetd mode, --ssl to forward SSL
|
||||
connexions (in fact normal HTTP at that stage) to port 80,
|
||||
and SSH connexions to port 22. This works because sslh
|
||||
considers that anything that is not SSH is SSL.
|
||||
considers that any protocol it doesn't recognise is SSL.
|
||||
|
||||
==== IP_TPROXY support ====
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <pwd.h>
|
||||
#include <syslog.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -32,31 +33,34 @@
|
||||
|
||||
int is_ssh_protocol(const char *p, int len);
|
||||
int is_openvpn_protocol(const char *p, int len);
|
||||
int is_tinc_protocol(const char *p, int len);
|
||||
int is_true(const char *p, int len) { return 1; }
|
||||
|
||||
struct proto protocols[] = {
|
||||
/* affected description service saddr probe */
|
||||
{ 0, "SSH", "sshd", {0}, is_ssh_protocol },
|
||||
{ 0, "OpenVPN", NULL, {0}, is_openvpn_protocol },
|
||||
{ 0, "ssh", "sshd", {0}, is_ssh_protocol },
|
||||
{ 0, "openvpn", NULL, {0}, is_openvpn_protocol },
|
||||
{ 0, "tinc", NULL, {0}, is_tinc_protocol },
|
||||
/* probe for SSL always successes: it's the default, and must be tried last
|
||||
**/
|
||||
{ 0, "SSL", NULL, {0}, is_true }
|
||||
{ 0, "ssl", NULL, {0}, is_true }
|
||||
};
|
||||
|
||||
|
||||
const char* USAGE_STRING =
|
||||
"sslh " VERSION "\n" \
|
||||
"usage:\n" \
|
||||
"\tsslh [-v] [-i] [-V] [-f]"
|
||||
"[-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
|
||||
"\t\t-s [sshhost:]port -l [sslhost:]port [-P pidfile]\n\n" \
|
||||
"\tsslh [-v] [-i] [-V] [-f]\n"
|
||||
"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
|
||||
"\t[--ssh <addr>] [--ssl <addr>] [--openvpn <addr>] [--tinc <addr>]\n\n" \
|
||||
"-v: verbose\n" \
|
||||
"-V: version\n" \
|
||||
"-f: foreground\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" \
|
||||
"-o: OpenVPN address: where to connect an OpenVPN connection.\n" \
|
||||
"-p: address and port to listen on. default: 0.0.0.0:443.\n Can be used several times to bind to several addresses.\n" \
|
||||
"--ssh: SSH address: where to connect an SSH connection.\n" \
|
||||
"--ssl: SSL address: where to connect an SSL connection.\n" \
|
||||
"--openvpn: OpenVPN address: where to connect an OpenVPN connection.\n" \
|
||||
"--tinc: tinc address: where to connect a tinc connection.\n" \
|
||||
"-P: PID file. Default: /var/run/sslh.pid.\n" \
|
||||
"-i: Run as a inetd service.\n" \
|
||||
"";
|
||||
@@ -71,38 +75,59 @@ int verbose = 0;
|
||||
int probing_timeout = 2;
|
||||
int inetd = 0;
|
||||
int foreground = 0;
|
||||
struct sockaddr addr_listen;
|
||||
int numeric = 0;
|
||||
char *user_name, *pid_file;
|
||||
|
||||
struct sockaddr_storage *addr_listen = NULL; /* what addresses do we listen to? */
|
||||
int num_addr_listen = 0; /* How many addresses do we listen to? */
|
||||
|
||||
#ifdef LIBWRAP
|
||||
#include <tcpd.h>
|
||||
int allow_severity =0, deny_severity = 0;
|
||||
#endif
|
||||
|
||||
|
||||
/* check result and die, printing the offending address and error */
|
||||
void check_res_dumpdie(int res, struct sockaddr_storage *sock, char* syscall)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
/* Starts a listening socket on specified address.
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s:%s: %s\n",
|
||||
sprintaddr(buf, sizeof(buf), sock),
|
||||
syscall,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Starts listening sockets on specified addresses.
|
||||
* IN: addr[], num_addr
|
||||
* OUT: sockfd[]
|
||||
* Bound file descriptors are returned in alread-allocated *sockfd pointer
|
||||
Returns file descriptor
|
||||
*/
|
||||
int start_listen_socket(struct sockaddr *addr)
|
||||
void start_listen_sockets(int sockfd[], struct sockaddr_storage addr[], int num_addr)
|
||||
{
|
||||
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
|
||||
int sockfd, res, reuse;
|
||||
struct sockaddr_storage *saddr;
|
||||
int i, res, reuse;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
CHECK_RES_DIE(sockfd, "socket");
|
||||
for (i = 0; i < num_addr; i++) {
|
||||
saddr = &addr[i];
|
||||
|
||||
reuse = 1;
|
||||
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
|
||||
CHECK_RES_DIE(res, "setsockopt");
|
||||
sockfd[i] = socket(saddr->ss_family, SOCK_STREAM, 0);
|
||||
check_res_dumpdie(sockfd[i], saddr, "socket");
|
||||
|
||||
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
|
||||
CHECK_RES_DIE(res, "bind");
|
||||
reuse = 1;
|
||||
res = setsockopt(sockfd[i], SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
|
||||
check_res_dumpdie(res, saddr, "setsockopt");
|
||||
|
||||
res = listen (sockfd, 50);
|
||||
CHECK_RES_DIE(res, "listen");
|
||||
res = bind (sockfd[i], (struct sockaddr*)saddr, sizeof(*saddr));
|
||||
check_res_dumpdie(res, saddr, "bind");
|
||||
|
||||
return sockfd;
|
||||
res = listen (sockfd[i], 50);
|
||||
check_res_dumpdie(res, saddr, "listen");
|
||||
}
|
||||
}
|
||||
|
||||
/* Store some data to write to the queue later */
|
||||
@@ -260,6 +285,13 @@ int is_openvpn_protocol (const char*p,int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is the buffer the beginning of a tinc connections?
|
||||
* (protocol is undocumented, but starts with "0 " in 1.0.15)
|
||||
* */
|
||||
int is_tinc_protocol( const char *p, int len)
|
||||
{
|
||||
return !strncmp(p, "0 ", len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the beginning of data coming from the client connection and check if
|
||||
@@ -290,17 +322,26 @@ T_PROTO_ID probe_client_protocol(struct connection *cnx)
|
||||
}
|
||||
}
|
||||
|
||||
/* If none worked, return the last one */
|
||||
return ARRAY_SIZE(protocols) - 1;
|
||||
/* If none worked, return the first one affected (that's completely
|
||||
* arbitrary) */
|
||||
for (i = 0; i < ARRAY_SIZE(protocols); i++)
|
||||
if (protocols[i].affected)
|
||||
return i;
|
||||
|
||||
/* At this stage... nothing is affected. This shouldn't happen as we check
|
||||
* at least one target exists when we parse the commnand line */
|
||||
fprintf(stderr, "FATAL: No protocol affected. This should not happen.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* returns a string that prints the IP and port of the sockaddr */
|
||||
char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
|
||||
char* sprintaddr(char* buf, size_t size, struct sockaddr_storage* s)
|
||||
{
|
||||
char addr_str[1024];
|
||||
char host[NI_MAXHOST], serv[NI_MAXSERV];
|
||||
|
||||
getnameinfo((struct sockaddr*)s, sizeof(*s), host, sizeof(host), serv, sizeof(serv), numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
|
||||
snprintf(buf, size, "%s:%s", host, serv);
|
||||
|
||||
inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
|
||||
snprintf(buf, size, "%s:%d", addr_str, ntohs(((struct sockaddr_in*)s)->sin_port));
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -308,28 +349,26 @@ char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
|
||||
sock: socket address to which to copy the addr
|
||||
fullname: input string -- it gets clobbered
|
||||
*/
|
||||
void resolve_name(struct sockaddr *sock, char* fullname)
|
||||
void resolve_name(struct sockaddr_storage *sock, char* fullname)
|
||||
{
|
||||
struct addrinfo *addr, hint;
|
||||
char *serv, *host;
|
||||
int res;
|
||||
|
||||
char *sep = strchr(fullname, ':');
|
||||
char *sep = strrchr(fullname, ':');
|
||||
|
||||
if (!sep) /* No separator: parameter is just a port */
|
||||
{
|
||||
serv = fullname;
|
||||
fprintf(stderr, "names must be fully specified as hostname:port\n");
|
||||
exit(1);
|
||||
}
|
||||
else {
|
||||
host = fullname;
|
||||
serv = sep+1;
|
||||
*sep = 0;
|
||||
}
|
||||
|
||||
host = fullname;
|
||||
serv = sep+1;
|
||||
*sep = 0;
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
hint.ai_family = PF_INET;
|
||||
hint.ai_family = PF_UNSPEC;
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
|
||||
res = getaddrinfo(host, serv, &hint, &addr);
|
||||
@@ -363,19 +402,40 @@ void log_message(int type, char* msg, ...)
|
||||
/* syslogs who connected to where */
|
||||
void log_connection(struct connection *cnx)
|
||||
{
|
||||
struct sockaddr peeraddr, localaddr;
|
||||
struct sockaddr_storage peeraddr; /* Who's connecting to sshd */
|
||||
struct sockaddr_storage listenaddr; /* Where is it connecting to */
|
||||
struct sockaddr_storage forwardfromaddr; /* Where is it forwarded from */
|
||||
struct sockaddr_storage targetaddr; /* Where is it forwarded to */
|
||||
socklen_t size = sizeof(peeraddr);
|
||||
char buf[64], buf2[64];
|
||||
#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
|
||||
char buf[MAX_NAMELENGTH], buf2[MAX_NAMELENGTH], buf3[MAX_NAMELENGTH], buf4[MAX_NAMELENGTH];
|
||||
int res;
|
||||
|
||||
res = getpeername(cnx->q[0].fd, &peeraddr, &size);
|
||||
memset(&peeraddr, 0, sizeof(peeraddr));
|
||||
memset(&listenaddr, 0, sizeof(listenaddr));
|
||||
memset(&forwardfromaddr, 0, sizeof(forwardfromaddr));
|
||||
memset(&targetaddr, 0, sizeof(targetaddr));
|
||||
|
||||
res = getpeername(cnx->q[0].fd, (struct sockaddr*)&peeraddr, &size);
|
||||
if (res == -1) return; /* that should never happen, right? */
|
||||
|
||||
res = getpeername(cnx->q[1].fd, &localaddr, &size);
|
||||
if (res == -1) return; /* that should never happen, right? */
|
||||
size = sizeof(listenaddr);
|
||||
res = getsockname(cnx->q[0].fd, (struct sockaddr*)&listenaddr, &size);
|
||||
if (res == -1) return;
|
||||
|
||||
log_message(LOG_INFO, "connection from %s forwarded to %s\n",
|
||||
sprintaddr(buf, sizeof(buf), &peeraddr), sprintaddr(buf2, sizeof(buf2), &localaddr));
|
||||
size = sizeof(targetaddr);
|
||||
res = getpeername(cnx->q[1].fd, (struct sockaddr*)&targetaddr, &size);
|
||||
if (res == -1) return;
|
||||
|
||||
size = sizeof(forwardfromaddr);
|
||||
res = getsockname(cnx->q[1].fd, (struct sockaddr*)&forwardfromaddr, &size);
|
||||
if (res == -1) return;
|
||||
|
||||
log_message(LOG_INFO, "connection from %s to %s forwarded from %s to %s\n",
|
||||
sprintaddr(buf, sizeof(buf), &peeraddr),
|
||||
sprintaddr(buf2, sizeof(buf2), &listenaddr),
|
||||
sprintaddr(buf3, sizeof(buf3), &forwardfromaddr),
|
||||
sprintaddr(buf4, sizeof(buf4), &targetaddr));
|
||||
|
||||
}
|
||||
|
||||
@@ -386,27 +446,38 @@ void log_connection(struct connection *cnx)
|
||||
*
|
||||
* Returns -1 if access is denied, 0 otherwise
|
||||
*/
|
||||
int check_access_rights(int in_socket, const char* service)
|
||||
int check_access_rights(int in_socket, char* service)
|
||||
{
|
||||
#ifdef LIBWRAP
|
||||
struct sockaddr peeraddr;
|
||||
socklen_t size = sizeof(peeraddr);
|
||||
char addr_str[1024];
|
||||
struct hostent *host;
|
||||
struct in_addr addr;
|
||||
char addr_str[NI_MAXHOST], host[NI_MAXHOST];
|
||||
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);
|
||||
/* extract peer address */
|
||||
res = getnameinfo(&peeraddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
|
||||
if (res) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
|
||||
strcpy(addr_str, STRING_UNKNOWN);
|
||||
}
|
||||
/* extract peer name */
|
||||
strcpy(host, STRING_UNKNOWN);
|
||||
if (!numeric) {
|
||||
res = getnameinfo(&peeraddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
|
||||
if (res) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hosts_ctl(service, (host ? host->h_name : STRING_UNKNOWN), addr_str, STRING_UNKNOWN)) {
|
||||
if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "access denied\n");
|
||||
log_connection(in_socket, "access denied");
|
||||
log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
|
||||
close(in_socket);
|
||||
return -1;
|
||||
}
|
||||
@@ -489,14 +560,62 @@ void printsettings(void)
|
||||
sprintaddr(buf, sizeof(buf), &protocols[i].saddr),
|
||||
protocols[i].service);
|
||||
}
|
||||
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
|
||||
fprintf(stderr, "listening on:\n");
|
||||
for (i = 0; i < num_addr_listen; i++) {
|
||||
fprintf(stderr, "\t%s\n", sprintaddr(buf, sizeof(buf), &addr_listen[i]));
|
||||
}
|
||||
fprintf(stderr, "timeout to ssh: %d\n", probing_timeout);
|
||||
}
|
||||
|
||||
/* Adds protocols to the list of options, so command-line parsing uses the
|
||||
* protocol definition array
|
||||
* options: array of options to add to; must be big enough
|
||||
* n_opts: number of options in *options before calling (i.e. where to append)
|
||||
* prot: array of protocols
|
||||
* n_prots: number of protocols in *prot
|
||||
* */
|
||||
#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */
|
||||
void append_protocols(struct option *options, int n_opts, struct proto *prot, int n_prots)
|
||||
{
|
||||
int o, p;
|
||||
|
||||
for (o = n_opts, p = 0; p < n_prots; o++, p++) {
|
||||
options[o].name = prot[p].description;
|
||||
options[o].has_arg = required_argument;
|
||||
options[o].flag = 0;
|
||||
options[o].val = p + PROT_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_cmdline(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
int c, affected = 0;
|
||||
struct option const_options[] = {
|
||||
{ "inetd", no_argument, &inetd, 1 },
|
||||
{ "foreground", no_argument, &foreground, 1 },
|
||||
{ "verbose", no_argument, &verbose, 1 },
|
||||
{ "numeric", no_argument, &numeric, 1 },
|
||||
{ "user", required_argument, 0, 'u' },
|
||||
{ "pidfile", required_argument, 0, 'P' },
|
||||
{ "timeout", required_argument, 0, 't' },
|
||||
{ "listen", required_argument, 0, 'p' },
|
||||
};
|
||||
struct option all_options[ARRAY_SIZE(const_options) + ARRAY_SIZE(protocols) + 1];
|
||||
|
||||
memset(all_options, 0, sizeof(all_options));
|
||||
memcpy(all_options, const_options, sizeof(const_options));
|
||||
append_protocols(all_options, ARRAY_SIZE(const_options), protocols, ARRAY_SIZE(protocols));
|
||||
|
||||
while ((c = getopt_long_only(argc, argv, "t:l:s:o:T:p:VP:", all_options, NULL)) != -1) {
|
||||
if (c == 0) continue;
|
||||
|
||||
if (c >= PROT_SHIFT) {
|
||||
affected++;
|
||||
protocols[c - PROT_SHIFT].affected = 1;
|
||||
resolve_name(&protocols[c - PROT_SHIFT].saddr, optarg);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, "t:l:s:o:p:P:ivfVu:")) != EOF) {
|
||||
switch (c) {
|
||||
|
||||
case 't':
|
||||
@@ -504,34 +623,9 @@ void parse_cmdline(int argc, char* argv[])
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
resolve_name(&addr_listen, optarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
protocols[PROT_SSL].affected = 1;
|
||||
resolve_name(&protocols[PROT_SSL].saddr, optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
protocols[PROT_SSH].affected = 1;
|
||||
resolve_name(&protocols[PROT_SSH].saddr, optarg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
protocols[PROT_OPENVPN].affected = 1;
|
||||
resolve_name(&protocols[PROT_OPENVPN].saddr, optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
inetd = 1;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
foreground = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose += 1;
|
||||
num_addr_listen++;
|
||||
addr_listen = realloc(addr_listen, num_addr_listen * sizeof(addr_listen[0]));
|
||||
resolve_name(&addr_listen[num_addr_listen - 1], optarg);
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
@@ -551,6 +645,17 @@ void parse_cmdline(int argc, char* argv[])
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!affected) {
|
||||
fprintf(stderr, "At least one target protocol must be specified.\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
if (!num_addr_listen) {
|
||||
fprintf(stderr, "No listening address specified; use at least one -p option\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -560,22 +665,13 @@ int main(int argc, char *argv[])
|
||||
extern int optind;
|
||||
int res;
|
||||
|
||||
int listen_socket;
|
||||
int *listen_sockets;
|
||||
|
||||
/* Init defaults */
|
||||
char listen_str[] = "0.0.0.0:443";
|
||||
char ssl_str[] = "localhost:443";
|
||||
char ssh_str[] = "localhost:22";
|
||||
pid_file = "/var/run/sslh.pid";
|
||||
user_name = "nobody";
|
||||
foreground = 0;
|
||||
|
||||
resolve_name(&addr_listen, listen_str);
|
||||
protocols[PROT_SSL].affected = 1;
|
||||
resolve_name(&protocols[PROT_SSL].saddr, ssl_str);
|
||||
protocols[PROT_SSH].affected = 1;
|
||||
resolve_name(&protocols[PROT_SSH].saddr, ssh_str);
|
||||
|
||||
parse_cmdline(argc, argv);
|
||||
|
||||
if (inetd)
|
||||
@@ -588,7 +684,9 @@ int main(int argc, char *argv[])
|
||||
if (verbose)
|
||||
printsettings();
|
||||
|
||||
listen_socket = start_listen_socket(&addr_listen);
|
||||
listen_sockets = malloc(num_addr_listen * sizeof(*listen_sockets));
|
||||
start_listen_sockets(listen_sockets, addr_listen, num_addr_listen);
|
||||
free(addr_listen);
|
||||
|
||||
if (!foreground)
|
||||
if (fork() > 0) exit(0); /* Detach */
|
||||
@@ -608,7 +706,7 @@ int main(int argc, char *argv[])
|
||||
/* Open syslog connection */
|
||||
setup_syslog(argv[0]);
|
||||
|
||||
main_loop(listen_socket);
|
||||
main_loop(listen_sockets, num_addr_listen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -52,15 +52,16 @@ enum connection_state {
|
||||
typedef enum protocol_type {
|
||||
PROT_SSH,
|
||||
PROT_OPENVPN,
|
||||
PROT_TINC,
|
||||
PROT_SSL,
|
||||
} T_PROTO_ID;
|
||||
|
||||
/* For each protocol we need: */
|
||||
struct proto {
|
||||
int affected; /* are we actually using it? */
|
||||
char* description; /* a string that says what it is (for logging) */
|
||||
char* description; /* a string that says what it is (for logging and command-line parsing) */
|
||||
char* service; /* service name to do libwrap checks */
|
||||
struct sockaddr saddr; /* where to switch that protocol */
|
||||
struct sockaddr_storage saddr; /* where to switch that protocol */
|
||||
int (*probe)(const char*, int); /* function to probe that protocol */
|
||||
};
|
||||
|
||||
@@ -93,13 +94,13 @@ struct connection {
|
||||
|
||||
/* common.c */
|
||||
void init_cnx(struct connection *cnx);
|
||||
int start_listen_socket(struct sockaddr *addr);
|
||||
void start_listen_sockets(int sockfd[], struct sockaddr_storage addr[], int num_addr);
|
||||
int fd2fd(struct queue *target, struct queue *from);
|
||||
char* sprintaddr(char* buf, size_t size, struct sockaddr_storage* s);
|
||||
void resolve_name(struct sockaddr_storage *sock, char* fullname) ;
|
||||
T_PROTO_ID probe_client_protocol(struct connection *cnx);
|
||||
char* sprintaddr(char* buf, size_t size, struct sockaddr* s);
|
||||
void resolve_name(struct sockaddr *sock, char* fullname) ;
|
||||
void log_connection(struct connection *cnx);
|
||||
int check_access_rights(int in_socket, const char* service);
|
||||
int check_access_rights(int in_socket, char* service);
|
||||
void setup_signals(void);
|
||||
void setup_syslog(char* bin_name);
|
||||
void drop_privileges(char* user_name);
|
||||
@@ -114,7 +115,7 @@ int defer_write(struct queue *q, void* data, int data_size);
|
||||
int flush_defered(struct queue *q);
|
||||
|
||||
extern int probing_timeout, verbose, inetd;
|
||||
extern struct sockaddr addr_listen, addr_ssl, addr_ssh, addr_openvpn;
|
||||
extern struct sockaddr_storage *addr_listen, addr_ssl, addr_ssh, addr_openvpn;
|
||||
extern const char* USAGE_STRING;
|
||||
extern char* user_name, *pid_file;
|
||||
extern const char* server_type;
|
||||
@@ -122,6 +123,6 @@ extern const char* server_type;
|
||||
/* sslh-fork.c */
|
||||
void start_shoveler(int);
|
||||
|
||||
void main_loop(int);
|
||||
void main_loop(int *listen_sockets, int num_addr_listen);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ DAEMON=$PREFIX/sbin/sslh
|
||||
start()
|
||||
{
|
||||
echo "Start services: sslh"
|
||||
$DAEMON -u nobody -p ${LISTEN} -s ${SSH} -l ${SSL}
|
||||
$DAEMON --user nobody --listen ${LISTEN} --ssh ${SSH} --ssl ${SSL}
|
||||
logger -t ${tag} -p ${facility} -i 'Started sslh'
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
SSLH="/usr/local/sbin/sslh"
|
||||
PIDFILE="/var/run/sslh"
|
||||
|
||||
OPTIONS="-p 0.0.0.0:8443 -l 127.0.0.1:443 -s 127.0.0.1:22"
|
||||
OPTIONS="-p 0.0.0.0:8443 --ssl 127.0.0.1:443 --ssh 127.0.0.1:22"
|
||||
|
||||
if [ -f /etc/sysconfig/sslh ]; then
|
||||
. /etc/sysconfig/sslh
|
||||
|
||||
+21
-17
@@ -68,7 +68,7 @@ void start_shoveler(int in_socket)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
struct sockaddr *saddr;
|
||||
struct sockaddr_storage *saddr;
|
||||
int res;
|
||||
int out_socket;
|
||||
char *target;
|
||||
@@ -103,8 +103,8 @@ void start_shoveler(int in_socket)
|
||||
}
|
||||
|
||||
/* Connect the target socket */
|
||||
out_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
res = connect(out_socket, saddr, sizeof(addr_ssl));
|
||||
out_socket = socket(saddr->ss_family, SOCK_STREAM, 0);
|
||||
res = connect(out_socket, (struct sockaddr*)saddr, sizeof(addr_ssl));
|
||||
CHECK_RES_DIE(res, "connect");
|
||||
if (verbose)
|
||||
fprintf(stderr, "connected to something\n");
|
||||
@@ -126,23 +126,27 @@ void start_shoveler(int in_socket)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void main_loop(int listen_socket)
|
||||
void main_loop(int *listen_sockets, int num_addr_listen)
|
||||
{
|
||||
int in_socket;
|
||||
int in_socket, i;
|
||||
|
||||
while (1)
|
||||
{
|
||||
in_socket = accept(listen_socket, 0, 0);
|
||||
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
|
||||
for (i = 0; i < num_addr_listen; i++) {
|
||||
if (!fork()) {
|
||||
while (1)
|
||||
{
|
||||
in_socket = accept(listen_sockets[i], 0, 0);
|
||||
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
|
||||
|
||||
if (!fork())
|
||||
{
|
||||
close(listen_socket);
|
||||
start_shoveler(in_socket);
|
||||
exit(0);
|
||||
}
|
||||
close(in_socket);
|
||||
}
|
||||
if (!fork())
|
||||
{
|
||||
close(listen_sockets[i]);
|
||||
start_shoveler(in_socket);
|
||||
exit(0);
|
||||
}
|
||||
close(in_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The actual main is in common.c: it's the same for both version of
|
||||
|
||||
+19
-15
@@ -111,15 +111,15 @@ int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_
|
||||
}
|
||||
|
||||
/* Connect queue 1 of connection to SSL; returns new file descriptor */
|
||||
int connect_queue(struct connection *cnx, struct sockaddr *addr,
|
||||
int connect_queue(struct connection *cnx, struct sockaddr_storage *addr,
|
||||
char* cnx_name,
|
||||
fd_set *fds_r, fd_set *fds_w)
|
||||
{
|
||||
struct queue *q = &cnx->q[1];
|
||||
int res;
|
||||
|
||||
q->fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
res = connect(q->fd, addr, sizeof(*addr));
|
||||
q->fd = socket(addr->ss_family, SOCK_STREAM, 0);
|
||||
res = connect(q->fd, (struct sockaddr*)addr, sizeof(*addr));
|
||||
log_connection(cnx);
|
||||
if (res == -1) {
|
||||
tidy_connection(cnx, fds_r, fds_w);
|
||||
@@ -188,7 +188,7 @@ int is_fd_active(int fd, fd_set* set)
|
||||
* That way, each pair of file descriptor (read from one, write to the other)
|
||||
* is monitored either for read or for write, but never for both.
|
||||
*/
|
||||
void main_loop(int listen_socket)
|
||||
void main_loop(int *listen_sockets, int num_addr_listen)
|
||||
{
|
||||
fd_set fds_r, fds_w; /* reference fd sets (used to init the next 2) */
|
||||
fd_set readfds, writefds; /* working read and write fd sets */
|
||||
@@ -203,10 +203,12 @@ void main_loop(int listen_socket)
|
||||
|
||||
FD_ZERO(&fds_r);
|
||||
FD_ZERO(&fds_w);
|
||||
FD_SET(listen_socket, &fds_r);
|
||||
max_fd = listen_socket + 1;
|
||||
|
||||
set_nonblock(listen_socket);
|
||||
for (i = 0; i < num_addr_listen; i++) {
|
||||
FD_SET(listen_sockets[i], &fds_r);
|
||||
set_nonblock(listen_sockets[i]);
|
||||
}
|
||||
max_fd = listen_sockets[num_addr_listen-1] + 1;
|
||||
|
||||
cnx_num_alloc = getpagesize() / sizeof(struct connection);
|
||||
|
||||
@@ -231,16 +233,18 @@ void main_loop(int listen_socket)
|
||||
|
||||
|
||||
/* Check main socket for new connections */
|
||||
if (FD_ISSET(listen_socket, &readfds)) {
|
||||
in_socket = accept_new_connection(listen_socket, &cnx, &num_cnx);
|
||||
num_probing++;
|
||||
for (i = 0; i < num_addr_listen; i++) {
|
||||
if (FD_ISSET(listen_sockets[i], &readfds)) {
|
||||
in_socket = accept_new_connection(listen_sockets[i], &cnx, &num_cnx);
|
||||
num_probing++;
|
||||
|
||||
if (in_socket > 0) {
|
||||
FD_SET(in_socket, &fds_r);
|
||||
if (in_socket >= max_fd)
|
||||
max_fd = in_socket + 1;;
|
||||
if (in_socket > 0) {
|
||||
FD_SET(in_socket, &fds_r);
|
||||
if (in_socket >= max_fd)
|
||||
max_fd = in_socket + 1;;
|
||||
}
|
||||
FD_CLR(listen_sockets[i], &readfds);
|
||||
}
|
||||
FD_CLR(listen_socket, &readfds);
|
||||
}
|
||||
|
||||
/* Check all sockets for write activity */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
=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<-o> I<target address for OpenVPN>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f]
|
||||
sslh [ B<-t> I<num> ] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-o> I<target address for OpenVPN>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
@@ -57,69 +57,79 @@ defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-t> I<num>
|
||||
=item B<-t> I<num>, B<--timeout> I<num>
|
||||
|
||||
Timeout before a connection is considered to be SSH. Default
|
||||
is 2s.
|
||||
|
||||
=item B<-p> I<listening address>
|
||||
=item B<-p> I<listening address>, B<--listen> 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).
|
||||
This can be specified several times to bind B<sslh> to
|
||||
several addresses.
|
||||
|
||||
=item B<-l> I<target address for SSL>
|
||||
=item B<--ssl> I<target address>
|
||||
|
||||
Interface and port on which to forward SSL connection,
|
||||
typically I<localhost:443>.
|
||||
|
||||
Defaults to I<localhost:443> (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>
|
||||
=item B<--ssh> I<target address>
|
||||
|
||||
Interface and port on which to forward SSH connection,
|
||||
defaults to I<localhost:22>.
|
||||
Interface and port on which to forward SSH connections,
|
||||
typically I<localhost:22>.
|
||||
|
||||
=item B<-o> I<target address for OpenVPN>
|
||||
=item B<--openvpn> I<target address>
|
||||
|
||||
Interface and port on which to forward OpenVPN connections.
|
||||
This parameter is optional, and has no default. If not
|
||||
specified, incoming OpenVPN connections will not be detected
|
||||
as such and treated the same as SSL.
|
||||
Interface and port on which to forward OpenVPN connections,
|
||||
typically I<localhost:1194>.
|
||||
|
||||
=item B<-v>
|
||||
=item B<--tinc> I<target address>
|
||||
|
||||
Interface and port on which to forward tinc connections,
|
||||
typically I<localhost:655>.
|
||||
|
||||
This is experimental. If you use this feature, please report
|
||||
the results (even if it works!)
|
||||
|
||||
=item B<-v>, B<--verbose>
|
||||
|
||||
Increase verboseness.
|
||||
|
||||
=item B<-n>, B<--numeric>
|
||||
|
||||
Do not attempt to resolve hostnames: logs will contain IP
|
||||
addresses. This is mostly useful if the system's DNS is slow
|
||||
and running the I<sslh-select> variant, as DNS requests will
|
||||
hang all connections.
|
||||
|
||||
=item B<-V>
|
||||
|
||||
Prints B<sslh> version.
|
||||
|
||||
=item B<-u> I<username>
|
||||
=item B<-u> I<username>, B<--user> 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>
|
||||
=item B<-P> I<pidfile>, B<--pid-file> I<pidfile>
|
||||
|
||||
Specifies the file in which to write the PID of the main
|
||||
server. Defaults to I</var/run/sslh.pid>.
|
||||
|
||||
=item B<-i>
|
||||
=item B<-i>, B<--inetd>
|
||||
|
||||
Runs as an I<inetd> server. Options B<-P> (PID file), B<-p>
|
||||
(listen address), B<-u> (user) are ignored.
|
||||
|
||||
=item B<-f>
|
||||
=item B<-f>, B<--foreground>
|
||||
|
||||
Runs in foreground. The server will not fork and will remain connected
|
||||
to the terminal. Messages normally sent to B<syslog> will also be sent
|
||||
|
||||
Reference in new issue
Block a user