Permalink
Browse files

- sshttp: adding SNI mux

  • Loading branch information...
stealth committed Feb 21, 2017
1 parent a675245 commit 573b88149a0e49d66f396aece417c549af383c45
Showing with 169 additions and 22 deletions.
  1. +2 −2 config.h
  2. +21 −3 main.cc
  3. +24 −8 nf-setup
  4. +3 −3 socket.h
  5. +108 −4 sshttp.cc
  6. +11 −2 sshttp.h
View
@@ -1,5 +1,5 @@
#ifndef __config_h__
#define __config_h__
#ifndef sshttp_config_h
#define sshttp_config_h
#include <stdint.h>
View
24 main.cc
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2016 Sebastian Krahmer.
* Copyright (C) 2010-2017 Sebastian Krahmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -94,8 +94,13 @@ int main(int argc, char **argv)
{
int c;
int family = AF_INET;
map<string, uint16_t> sni2port;
uint16_t sni_port = 0;
string sni = "";
string::size_type idx = 0;
while ((c = getopt(argc, argv, "S:H:L:R:U:n:6l:iT")) != -1) {
while ((c = getopt(argc, argv, "S:H:L:R:U:n:6l:N:iT")) != -1) {
switch (c) {
case 'T':
Config::tproxy = 1;
@@ -127,8 +132,18 @@ int main(int argc, char **argv)
if (Config::laddr == "0.0.0.0")
Config::laddr = "::";
break;
case 'N':
sni = optarg;
idx = sni.find(":");
if (idx == string::npos || idx + 1 >= sni.size())
break;
sni_port = (uint16_t)strtoul(sni.c_str() + idx + 1, NULL, 10);
if (sni_port <= 0)
break;
sni2port[sni.substr(0, idx)] = sni_port;
break;
default:
printf("sshttpd [-n CPU cores] [-S ssh port] [-H http port] [-L lport] [-l laddr] [-6] ");
printf("sshttpd [-n CPU cores] [-S ssh port] [-H http port] [-L lport] [-l laddr] [-6] [-N SNI:port] ");
#ifdef USE_CAPS
printf("[-U user] [-R chroot]");
#endif
@@ -155,6 +170,9 @@ int main(int argc, char **argv)
exit(errno);
}
for (auto i = sni2port.begin(); i != sni2port.end(); ++i)
sh.add_sni(i->first, i->second);
NS_Misc::init_multicore();
NS_Misc::setup_multicore(Config::cores);
View
@@ -8,23 +8,39 @@
# '-L 25 -H 2525'
DEV=eth0
SSH_PORT=22
HTTP_PORT=8080
# The ports you want to mux:
# -S <port> -H <port> and any other -N SNI:<ports> (in case of HTTPS)
# do NOT add the -L port here
# standard SSH / HTTP mux looks like this (sshttpd -S 22 -H 8080 -L 80)
PORTS="22 8080"
# a SSH / HTTPS mux with https server on port 4433 and a drops
# on port 7350 looks like this (sshttpd -S 22 -H 4433 -L 443 -N drops.v2:7350)
#PORTS="22 4433 7350"
# SNI-only mux without SSH (sshttpd -S 0 -H 4433 -L 443 -N drops.v2:7350)
#PORTS="4433 7350"
#if it clashes with complex NATing rules, try this
#iptables -t mangle -F
#iptables -t nat -F
#iptables -t raw -F
modprobe nf_conntrack_ipv4 || true
iptables -t mangle -N DIVERT || true
# block HTTP/SSH direct access
iptables -A INPUT -i $DEV -p tcp --dport $SSH_PORT -j DROP
iptables -A INPUT -i $DEV -p tcp --dport $HTTP_PORT -j DROP
for p in $PORTS; do
echo "Setting up port $p ..."
# block direct access from outside
iptables -A INPUT -i $DEV -p tcp --dport $p -j DROP
# and divert anything back to sshttpd that comes from the muxed services
# so sshttpd can see it
iptables -t mangle -A OUTPUT -p tcp -o $DEV --sport $p -j DIVERT
done
iptables -t mangle -N DIVERT || true
iptables -t mangle -A OUTPUT -p tcp -o $DEV --sport $SSH_PORT -j DIVERT
iptables -t mangle -A OUTPUT -p tcp -o $DEV --sport $HTTP_PORT -j DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
View
@@ -29,8 +29,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _MY_SOCKET_H_
#define _MY_SOCKET_H_
#ifndef sshttp_socket_h
#define sshttp_socket_h
#include <sys/types.h>
#include <sys/socket.h>
@@ -60,5 +60,5 @@ int writen(int fd, const void *buf, size_t len);
} // namespace
#endif // _MY_SOCKET_H_
#endif
View
112 sshttp.cc
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2016 Sebastian Krahmer.
* Copyright (C) 2010-2017 Sebastian Krahmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -628,7 +628,7 @@ int sshttp::loop()
uint16_t sshttp::find_port(int fd)
{
int r = 0;
char buf[1024];
unsigned char buf[2048 + 1] = {0};
r = recv(fd, buf, sizeof(buf) - 1, MSG_PEEK);
@@ -638,10 +638,114 @@ uint16_t sshttp::find_port(int fd)
else if (r < 0)
return d_ssh_port;
if (strncmp(buf, "SSH-", 4) == 0)
if (memcmp(buf, "SSH-", 4) == 0)
return d_ssh_port;
// no string match? https! (covered by HTTP_PORT)
// SNI lookup table configured? Must be https
if (sni2port.size() > 0) {
uint16_t p = https_to_port(buf, r);
if (p > 0)
return p;
// In case we found a parsing error of the ClientHello or miss the SNI, pass it
// to the original https port
}
// no string match? http(s)! (https covered by HTTP_PORT)
return d_http_port;
}
// also returns 0 on error
// See rfc5246 and rfc6066 for the TLS ClientHello format
// Find the SNI TLS extension inside Client Hello and return the port
// that was assigned for it in the sni2port map
uint16_t sshttp::https_to_port(const unsigned char *chello, int bsize)
{
const unsigned char *ptr = chello, *end = chello + bsize;
// TLS record
if (end - ptr <= 5)
return 0;
ptr += 5;
// ClientHello?
if (*ptr != 1)
return 0;
++ptr;
if (end - ptr <= 5 + 32 + 1) // record + Random + session_id len
return 0;
ptr += 5 + 32;
uint8_t sessid_len = *ptr;
++ptr;
if (end - ptr <= sessid_len)
return 0;
ptr += sessid_len;
if (end - ptr <= 2) // cipher suite len
return 0;
uint16_t clen = ntohs(*(uint16_t *)ptr);
ptr += 2;
if (end - ptr <= clen)
return 0;
ptr += clen;
if (end - ptr <= 1) // compression len
return 0;
clen = *ptr;
++ptr;
if (end - ptr <= clen)
return 0;
ptr += clen;
if (end - ptr <= 2) // Extensions len (sum of all Ex.)
return 0;
// skip Extensions len and iterate over each extension until we find SNI
ptr += 2;
for (; ptr < end;) {
if (end - ptr <= 2) // Ex. Type
break;
uint16_t etype = ntohs(*(uint16_t *)ptr);
ptr += 2;
if (end - ptr <= 2) // Ex. Len
break;
clen = ntohs(*(uint16_t *)ptr);
ptr += 2;
if (end - ptr <= clen)
break;
// servername Ex. found? Go deeper to parse SNI Ex. (what a stupid protocol)
// Theoretically there could be a lot of Server Name Types and list of hosts, but
// we only allow "hostname" type and just one of them
if (etype == 0) {
if (end - ptr <= 2)
break;
clen = ntohs(*(uint16_t *)ptr); // Server Name List len
ptr += 2;
if (end - ptr <= clen || end - ptr <= 1) // 1 for Server Name Type
break;
if (*ptr != 0) // Server Name Type 0 -> Host Name
break;
++ptr;
if (end - ptr <= 2)
break;
clen = ntohs(*(uint16_t *)ptr); // hostname len
ptr += 2;
if (end - ptr < clen)
break;
string hostname = string(reinterpret_cast<const char *>(ptr), clen);
if (sni2port.count(hostname) > 0)
return sni2port[hostname];
break;
}
ptr += clen;
}
return 0;
}
View
@@ -30,8 +30,8 @@
* SUCH DAMAGE.
*/
#ifndef __sshttp_h__
#define __sshttp_h__
#ifndef sshttp_sshttp_h
#define sshttp_sshttp_h
#include <stdio.h>
#include <poll.h>
@@ -61,6 +61,8 @@ class sshttp {
std::map<int, struct status *> fd2state;
std::map<std::string, uint16_t> sni2port;
void cleanup(int);
void shutdown(int);
@@ -69,6 +71,8 @@ class sshttp {
uint16_t find_port(int);
uint16_t https_to_port(const unsigned char *, int);
public:
sshttp() : pfds(NULL), d_ssh_port(22), d_http_port(8080), d_local_port(80), now(0),
af(AF_INET), heavy_load(0), err("") {}
@@ -91,6 +95,11 @@ class sshttp {
int loop();
void add_sni(const std::string &s, uint16_t p)
{
sni2port[s] = p;
}
const char *why();
};

0 comments on commit 573b881

Please sign in to comment.