git ssb

0+

cel / sslh



Commit 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a

v1.12: 08MAY2012

	Added support for configuration file.

	New protocol probes can be defined using regular
	expressions that match the first packet sent by the
	client.

	sslh now connects timed out connections to the first
	configured protocol instead of 'ssh' (just make sure
	ssh is the first defined protocol).

	sslh now tries protocols in the order in which they
	are defined (just make sure sslh is the last defined
	protocol).
Yves Rutschle committed on 7/10/2013, 9:15:38 PM
Parent: 26b4bcd089f6c3a3c43d1380969c939e600b3ef2

Files changed

ChangeLogchanged
Makefilechanged
READMEchanged
common.cchanged
common.hchanged
sslh-fork.cchanged
sslh-main.cchanged
sslh-select.cchanged
sslh.podchanged
tchanged
t_loadchanged
example.cfgadded
probe.cadded
probe.hadded
ChangeLogView
@@ -1,4 +1,19 @@
1+v1.12: 08MAY2012
2+ Added support for configuration file.
3+
4+ New protocol probes can be defined using regular
5+ expressions that match the first packet sent by the
6+ client.
7+
8+ sslh now connects timed out connections to the first
9+ configured protocol instead of 'ssh' (just make sure
10+ ssh is the first defined protocol).
11+
12+ sslh now tries protocols in the order in which they
13+ are defined (just make sure sslh is the last defined
14+ protocol).
15+
116 v1.11: 21APR2012
217 WARNING: defaults have been removed for --user and
318 --pidfile options, update your start-up scripts!
419
MakefileView
@@ -1,7 +1,8 @@
11 # Configuration
22
3-VERSION="v1.11"
3+VERSION="v1.12"
4+USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
45 USELIBWRAP= # Use libwrap?
56 COV_TEST= # Perform test coverage?
67 PREFIX=/usr/local
78
@@ -16,17 +17,21 @@
1617
1718 CC = gcc
1819 CFLAGS=-Wall -g $(CFLAGS_COV)
1920
20-#LIBS=-lnet
2121 LIBS=
22-OBJS=common.o sslh-main.o
22+OBJS=common.o sslh-main.o probe.o
2323
2424 ifneq ($(strip $(USELIBWRAP)),)
2525 LIBS:=$(LIBS) -lwrap
2626 CFLAGS:=$(CFLAGS) -DLIBWRAP
2727 endif
2828
29+ifneq ($(strip $(USELIBCONFIG)),)
30+ LIBS:=$(LIBS) -lconfig
31+ CFLAGS:=$(CFLAGS) -DLIBCONFIG
32+endif
33+
2934 all: sslh $(MAN) echosrv
3035
3136 .c.o: *.h
3237 $(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -c $<
READMEView
@@ -1,18 +1,31 @@
11 ===== sslh -- A ssl/ssh multiplexer. =====
22
3-sslh accepts HTTP, HTTPS, SSH, OpenVPN, tinc and XMPP
4-connections on the same port. This makes it possible to
5-connect to any of these servers on port 443 (e.g. from
3+sslh accepts connections in HTTP, HTTPS, SSH, OpenVPN,
4+tinc, XMPP, or any other protocol that can be tested using a
5+regular expression, on the same port. This makes it possible
6+to connect to any of these servers on port 443 (e.g. from
67 inside a corporate firewall, which almost never block port
78 443) while still serving HTTPS on that port.
89
10+
911 ==== Compile and install ====
1012
1113 If you're lucky, the Makefile will work for you:
1214
1315 make install
1416
17+There are a couple of configuration options at the beginning
18+of the Makefile:
19+
20+ USELIBWRAP compiles support for host access control (see
21+ hosts_access(3)), you will need libwrap headers and
22+ library to compile (libwrap0-dev in Debian).
23+
24+ USELIBCONFIG compiles support for the configuration
25+ file. You will need libconfig headers to compile
26+ (libconfig8-dev in Debian).
27+
1528 The Makefile produces two different executables: sslh-fork
1629 and sslh-select.
1730
1831 sslh-fork forks a new process for each incoming connection.
common.cView
@@ -4,25 +4,9 @@
44 * No code here should assume whether sockets are blocking or not.
55 **/
66
77 #define _GNU_SOURCE
8-#include <sys/types.h>
9-#include <fcntl.h>
10-#include <string.h>
11-#include <unistd.h>
12-#include <stdlib.h>
138 #include <stdarg.h>
14-#include <stdio.h>
15-#include <signal.h>
16-#include <sys/socket.h>
17-#include <sys/wait.h>
18-#include <netinet/in.h>
19-#include <arpa/inet.h>
20-#include <netdb.h>
21-#include <pwd.h>
22-#include <syslog.h>
23-#include <libgen.h>
24-#include <getopt.h>
259
2610 #include "common.h"
2711
2812 /* Added to make the code compilable under CYGWIN
@@ -30,36 +14,8 @@
3014 #ifndef SA_NOCLDWAIT
3115 #define SA_NOCLDWAIT 0
3216 #endif
3317
34-int is_ssh_protocol(const char *p, int len);
35-int is_openvpn_protocol(const char *p, int len);
36-int is_tinc_protocol(const char *p, int len);
37-int is_xmpp_protocol(const char *p, int len);
38-int is_http_protocol(const char *p, int len);
39-int is_true(const char *p, int len) { return 1; }
40-
41-/* Table of all the protocols we know how to connect to.
42- *
43- * The first protocol in the table is where we connect in case of timeout
44- * (client didn't speak: typically this is SSH.)
45- *
46- * The last protocol in the table is where we connect if client spoke but we
47- * couldn't probe what it's saying.
48- */
49-struct proto protocols[] = {
50- /* affected description service saddr probe */
51- { 0, "ssh", "sshd", {0}, is_ssh_protocol },
52- { 0, "openvpn", NULL, {0}, is_openvpn_protocol },
53- { 0, "tinc", NULL, {0}, is_tinc_protocol },
54- { 0, "xmpp", NULL, {0}, is_xmpp_protocol },
55- { 0, "http", NULL, {0}, is_http_protocol },
56- /* probe for SSL always successes: it's the default, and must be tried last
57- **/
58- { 0, "ssl", NULL, {0}, is_true }
59-};
60-int num_known_protocols = ARRAY_SIZE(protocols);
61-
6218 /*
6319 * Settings that depend on the command line. They're set in main(), but also
6420 * used in other places in common.c, and it'd be heavy-handed to pass it all as
6521 * parameters
@@ -68,9 +24,9 @@
6824 int probing_timeout = 2;
6925 int inetd = 0;
7026 int foreground = 0;
7127 int numeric = 0;
72-char *user_name, *pid_file;
28+const char *user_name, *pid_file, *rule_filename;
7329
7430 struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
7531
7632 #ifdef LIBWRAP
@@ -140,9 +96,9 @@
14096 }
14197
14298 /* Connect to first address that works and returns a file descriptor, or -1 if
14399 * none work. cnx_name points to the name of the service (for logging) */
144-int connect_addr(struct addrinfo *addr, char* cnx_name)
100+int connect_addr(struct addrinfo *addr, const char* cnx_name)
145101 {
146102 struct addrinfo *a;
147103 char buf[NI_MAXHOST];
148104 int fd, res;
@@ -290,125 +246,8 @@
290246
291247 return size_w;
292248 }
293249
294-/* If the client wrote something first, read it and check if it's a SSH banner.
295- * Data is left in appropriate defered write buffer.
296- */
297-int is_ssh_protocol(const char *p, int len)
298-{
299- if (!strncmp(p, "SSH-", 4)) {
300- return 1;
301- }
302- return 0;
303-}
304-
305-/* Is the buffer the beginning of an OpenVPN connection?
306- * (code lifted from OpenVPN port-share option)
307- */
308-int is_openvpn_protocol (const char*p,int len)
309-{
310-#define P_OPCODE_SHIFT 3
311-#define P_CONTROL_HARD_RESET_CLIENT_V2 7
312- if (len >= 3)
313- {
314- return p[0] == 0
315- && p[1] >= 14
316- && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
317- }
318- else if (len >= 2)
319- {
320- return p[0] == 0 && p[1] >= 14;
321- }
322- else
323- return 0;
324-}
325-
326-/* Is the buffer the beginning of a tinc connections?
327- * (protocol is undocumented, but starts with "0 " in 1.0.15)
328- * */
329-int is_tinc_protocol( const char *p, int len)
330-{
331- return !strncmp(p, "0 ", 2);
332-}
333-
334-/* Is the buffer the beginning of a jabber (XMPP) connections?
335- * (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
336- * clients, just checking first frame containing "jabber" in xml entity)
337- * */
338-int is_xmpp_protocol( const char *p, int len)
339-{
340- return strstr(p, "jabber") ? 1 : 0;
341-}
342-
343-int probe_http_method(const char *p, const char *opt)
344-{
345- return !strncmp(p, opt, strlen(opt)-1);
346-}
347-
348-/* Is the buffer the beginnin of an HTTP connection? */
349-int is_http_protocol(const char *p, int len)
350-{
351- /* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
352- if (strstr(p, "HTTP"))
353- return 1;
354-
355- /* Otherwise it could be HTTP/1.0 without version: check if it's got an
356- * HTTP method (RFC2616 5.1.1) */
357- probe_http_method(p, "OPTIONS");
358- probe_http_method(p, "GET");
359- probe_http_method(p, "HEAD");
360- probe_http_method(p, "POST");
361- probe_http_method(p, "PUT");
362- probe_http_method(p, "DELETE");
363- probe_http_method(p, "TRACE");
364- probe_http_method(p, "CONNECT");
365-
366- return 0;
367-}
368-
369-
370-/*
371- * Read the beginning of data coming from the client connection and check if
372- * it's a known protocol. Then leave the data on the defered
373- * write buffer of the connection and returns the protocol index in the
374- * protocols[] array *
375- */
376-T_PROTO_ID probe_client_protocol(struct connection *cnx)
377-{
378- char buffer[BUFSIZ];
379- int n, i;
380-
381- n = read(cnx->q[0].fd, buffer, sizeof(buffer));
382- /* It's possible that read() returns an error, e.g. if the client
383- * disconnected between the previous call to select() and now. If that
384- * happens, we just connect to the default protocol so the caller of this
385- * function does not have to deal with a specific failure condition (the
386- * connection will just fail later normally). */
387- if (n > 0) {
388- defer_write(&cnx->q[1], buffer, n);
389-
390- for (i = 0; i < ARRAY_SIZE(protocols); i++) {
391- if (protocols[i].affected) {
392- if (protocols[i].probe(buffer, n)) {
393- return i;
394- }
395- }
396- }
397- }
398-
399- /* If none worked, return the first one affected (that's completely
400- * arbitrary) */
401- for (i = 0; i < ARRAY_SIZE(protocols); i++)
402- if (protocols[i].affected)
403- return i;
404-
405- /* At this stage... nothing is affected. This shouldn't happen as we check
406- * at least one target exists when we parse the commnand line */
407- fprintf(stderr, "FATAL: No protocol affected. This should not happen.\n");
408- exit(1);
409-}
410-
411250 /* returns a string that prints the IP and port of the sockaddr */
412251 char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
413252 {
414253 char host[NI_MAXHOST], serv[NI_MAXSERV];
@@ -438,35 +277,48 @@
438277
439278 return buf;
440279 }
441280
281+/* Turns a hostname and port (or service) into a list of struct addrinfo
282+ * returns 0 on success, -1 otherwise and logs error
283+ **/
284+int resolve_split_name(struct addrinfo **out, const char* host, const char* serv)
285+{
286+ struct addrinfo hint;
287+ int res;
288+
289+ memset(&hint, 0, sizeof(hint));
290+ hint.ai_family = PF_UNSPEC;
291+ hint.ai_socktype = SOCK_STREAM;
292+
293+ res = getaddrinfo(host, serv, &hint, out);
294+ if (res)
295+ log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
296+ return res;
297+}
298+
442299 /* turns a "hostname:port" string into a list of struct addrinfo;
443300 out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done
444301 fullname: input string -- it gets clobbered
445302 */
446303 void resolve_name(struct addrinfo **out, char* fullname)
447304 {
448- struct addrinfo hint;
449305 char *serv, *host;
450306 int res;
451307
452308 char *sep = strrchr(fullname, ':');
453309
454310 if (!sep) /* No separator: parameter is just a port */
455311 {
456- fprintf(stderr, "names must be fully specified as hostname:port\n");
312+ fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname);
457313 exit(1);
458314 }
459315
460316 host = fullname;
461317 serv = sep+1;
462318 *sep = 0;
463319
464- memset(&hint, 0, sizeof(hint));
465- hint.ai_family = PF_UNSPEC;
466- hint.ai_socktype = SOCK_STREAM;
467-
468- res = getaddrinfo(host, serv, &hint, out);
320+ res = resolve_split_name(out, host, serv);
469321 if (res) {
470322 fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
471323 if (res == EAI_SERVICE)
472324 fprintf(stderr, "(Check you have specified all ports)\n");
@@ -532,9 +384,9 @@
532384 * apply the rules itself.
533385 *
534386 * Returns -1 if access is denied, 0 otherwise
535387 */
536-int check_access_rights(int in_socket, char* service)
388+int check_access_rights(int in_socket, const char* service)
537389 {
538390 #ifdef LIBWRAP
539391 struct sockaddr peeraddr;
540392 socklen_t size = sizeof(peeraddr);
@@ -571,9 +423,8 @@
571423 #endif
572424 return 0;
573425 }
574426
575-
576427 void setup_signals(void)
577428 {
578429 int res;
579430 struct sigaction action;
@@ -585,20 +436,25 @@
585436 action.sa_flags = SA_NOCLDWAIT;
586437 res = sigaction(SIGCHLD, &action, NULL);
587438 CHECK_RES_DIE(res, "sigaction");
588439
589-
590440 /* Set SIGTERM to exit. For some reason if it's not set explicitely,
591441 * coverage information is lost when killing the process */
592442 memset(&action, 0, sizeof(action));
593443 action.sa_handler = exit;
594444 res = sigaction(SIGTERM, &action, NULL);
595445 CHECK_RES_DIE(res, "sigaction");
446+
447+ /* Ignore SIGPIPE . */
448+ action.sa_handler = SIG_IGN;
449+ res = sigaction(SIGPIPE, &action, NULL);
450+ CHECK_RES_DIE(res, "sigaction");
451+
596452 }
597453
598454 /* Open syslog connection with appropriate banner;
599455 * banner is made up of basename(bin_name)+"[pid]" */
600-void setup_syslog(char* bin_name) {
456+void setup_syslog(const char* bin_name) {
601457 char *name1, *name2;
602458
603459 name1 = strdup(bin_name);
604460 asprintf(&name2, "%s[%d]", basename(name1), getpid());
@@ -609,9 +465,9 @@
609465 log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
610466 }
611467
612468 /* We don't want to run as root -- drop priviledges if required */
613-void drop_privileges(char* user_name)
469+void drop_privileges(const char* user_name)
614470 {
615471 int res;
616472 struct passwd *pw = getpwnam(user_name);
617473 if (!pw) {
@@ -627,9 +483,9 @@
627483 CHECK_RES_DIE(res, "setuid");
628484 }
629485
630486 /* Writes my PID */
631-void write_pid_file(char* pidfile)
487+void write_pid_file(const char* pidfile)
632488 {
633489 FILE *f;
634490
635491 f = fopen(pidfile, "w");
@@ -641,46 +497,4 @@
641497 fprintf(f, "%d\n", getpid());
642498 fclose(f);
643499 }
644500
645-void printsettings(void)
646-{
647- char buf[NI_MAXHOST];
648- struct addrinfo *a;
649- int i;
650-
651- for (i = 0; i < ARRAY_SIZE(protocols); i++) {
652- if (protocols[i].affected)
653- fprintf(stderr,
654- "%s addr: %s. libwrap service: %s family %d %d\n",
655- protocols[i].description,
656- sprintaddr(buf, sizeof(buf), &protocols[i].saddr),
657- protocols[i].service,
658- protocols[i].saddr.ai_family,
659- protocols[i].saddr.ai_addr->sa_family);
660- }
661- fprintf(stderr, "listening on:\n");
662- for (a = addr_listen; a; a = a->ai_next) {
663- fprintf(stderr, "\t%s\n", sprintaddr(buf, sizeof(buf), a));
664- }
665- fprintf(stderr, "timeout to ssh: %d\n", probing_timeout);
666-}
667-
668-/* Adds protocols to the list of options, so command-line parsing uses the
669- * protocol definition array
670- * options: array of options to add to; must be big enough
671- * n_opts: number of options in *options before calling (i.e. where to append)
672- * prot: array of protocols
673- * n_prots: number of protocols in *prot
674- * */
675-void append_protocols(struct option *options, int n_opts, struct proto *prot, int n_prots)
676-{
677- int o, p;
678-
679- for (o = n_opts, p = 0; p < n_prots; o++, p++) {
680- options[o].name = prot[p].description;
681- options[o].has_arg = required_argument;
682- options[o].flag = 0;
683- options[o].val = p + PROT_SHIFT;
684- }
685-}
686-
common.hView
@@ -1,4 +1,8 @@
1+#ifndef __COMMON_H_
2+#define __COMMON_H_
3+
4+
15 #define _GNU_SOURCE
26 #include <sys/types.h>
37 #include <fcntl.h>
48 #include <errno.h>
@@ -46,23 +50,8 @@
4650 ST_PROBING=1, /* Waiting for timeout to find where to forward */
4751 ST_SHOVELING /* Connexion is established */
4852 };
4953
50-typedef int T_PROTO_ID; /* Index into protocols[] array */
51-
52-/* For each protocol we need: */
53-struct proto {
54- int affected; /* are we actually using it? */
55- char* description; /* a string that says what it is (for logging and command-line parsing) */
56- char* service; /* service name to do libwrap checks */
57- struct addrinfo saddr; /* where to switch that protocol */
58- int (*probe)(const char*, int); /* function to probe that protocol */
59-};
60-
61-/* A table in common.c contains all the known protocols */
62-extern struct proto protocols[];
63-extern int num_known_protocols;
64-
6554 /* this is used to pass protocols through the command-line parameter parsing */
6655 #define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */
6756
6857 /* A 'queue' is composed of a file descriptor (which can be read from or
@@ -90,25 +79,23 @@
9079
9180
9281 /* common.c */
9382 void init_cnx(struct connection *cnx);
94-int connect_addr(struct addrinfo *addr, char* cnx_name);
83+int connect_addr(struct addrinfo *addr, const char* cnx_name);
9584 int fd2fd(struct queue *target, struct queue *from);
9685 char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
9786 void resolve_name(struct addrinfo **out, char* fullname);
98-T_PROTO_ID probe_client_protocol(struct connection *cnx);
87+struct proto* probe_client_protocol(struct connection *cnx);
9988 void log_connection(struct connection *cnx);
100-int check_access_rights(int in_socket, char* service);
89+int check_access_rights(int in_socket, const char* service);
10190 void setup_signals(void);
102-void setup_syslog(char* bin_name);
103-void drop_privileges(char* user_name);
104-void write_pid_file(char* pidfile);
105-void printsettings(void);
106-void parse_cmdline(int argc, char* argv[]);
91+void setup_syslog(const char* bin_name);
92+void drop_privileges(const char* user_name);
93+void write_pid_file(const char* pidfile);
10794 void log_message(int type, char* msg, ...);
10895 void dump_connection(struct connection *cnx);
96+int resolve_split_name(struct addrinfo **out, const char* hostname, const char* port);
10997
110-void append_protocols(struct option *options, int n_opts, struct proto *prot, int n_prots);
11198 int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list);
11299
113100 int defer_write(struct queue *q, void* data, int data_size);
114101 int flush_defered(struct queue *q);
@@ -116,11 +103,13 @@
116103 extern int probing_timeout, verbose, inetd, foreground, numeric;
117104 extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn;
118105 extern struct addrinfo *addr_listen;
119106 extern const char* USAGE_STRING;
120-extern char* user_name, *pid_file;
107+extern const char* user_name, *pid_file, *rule_filename;
121108 extern const char* server_type;
122109
123110 /* sslh-fork.c */
124111 void start_shoveler(int);
125112
126113 void main_loop(int *listen_sockets, int num_addr_listen);
114+
115+#endif
sslh-fork.cView
@@ -1,8 +1,8 @@
11 /*
2- Reimplementation of sslh in C
2+ sslh-fork: forking server
33
4-# Copyright (C) 2007-2011 Yves Rutschle
4+# Copyright (C) 2007-2012 Yves Rutschle
55 #
66 # This program is free software; you can redistribute it
77 # and/or modify it under the terms of the GNU General Public
88 # License as published by the Free Software Foundation; either
@@ -20,8 +20,9 @@
2020
2121 */
2222
2323 #include "common.h"
24+#include "probe.h"
2425
2526 const char* server_type = "sslh-fork";
2627
2728 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
@@ -71,9 +72,9 @@
7172 struct addrinfo *saddr;
7273 int res;
7374 int out_socket;
7475 struct connection cnx;
75- T_PROTO_ID prot;
76+ struct proto *prot;
7677
7778 init_cnx(&cnx);
7879
7980 FD_ZERO(&fds);
@@ -90,19 +91,19 @@
9091 /* Received data: figure out what protocol it is */
9192 prot = probe_client_protocol(&cnx);
9293 } else {
9394 /* Timed out: it's necessarily SSH */
94- prot = 0;
95+ prot = timeout_protocol();
9596 }
9697
97- saddr = &protocols[prot].saddr;
98- if (protocols[prot].service &&
99- check_access_rights(in_socket, protocols[prot].service)) {
98+ saddr = prot->saddr;
99+ if (prot->service &&
100+ check_access_rights(in_socket, prot->service)) {
100101 exit(0);
101102 }
102103
103104 /* Connect the target socket */
104- out_socket = connect_addr(saddr, protocols[prot].description);
105+ out_socket = connect_addr(saddr, prot->description);
105106 CHECK_RES_DIE(out_socket, "connect");
106107
107108 cnx.q[1].fd = out_socket;
108109
sslh-main.cView
@@ -1,8 +1,8 @@
11 /*
2-# main: processing of command line options and start the main loop.
2+# main: processing of config file, command line options and start the main loop.
33 #
4-# Copyright (C) 2007-2011 Yves Rutschle
4+# Copyright (C) 2007-2012 Yves Rutschle
55 #
66 # This program is free software; you can redistribute it
77 # and/or modify it under the terms of the GNU General Public
88 # License as published by the Free Software Foundation; either
@@ -20,91 +20,371 @@
2020
2121 */
2222
2323 #define _GNU_SOURCE
24-#include <sys/types.h>
25-#include <fcntl.h>
26-#include <string.h>
27-#include <unistd.h>
28-#include <stdlib.h>
29-#include <stdarg.h>
30-#include <stdio.h>
31-#include <signal.h>
32-#include <sys/socket.h>
33-#include <sys/wait.h>
34-#include <netinet/in.h>
35-#include <arpa/inet.h>
36-#include <netdb.h>
37-#include <pwd.h>
38-#include <syslog.h>
39-#include <libgen.h>
40-#include <getopt.h>
24+#ifdef LIBCONFIG
25+#include <libconfig.h>
26+#endif
27+#include <regex.h>
4128
4229 #include "common.h"
30+#include "probe.h"
4331
4432 const char* USAGE_STRING =
4533 "sslh " VERSION "\n" \
4634 "usage:\n" \
47-"\tsslh [-v] [-i] [-V] [-f] [-n]\n"
35+"\tsslh [-v] [-i] [-V] [-f] [-n] [-F <file>]\n"
4836 "\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
49-"%s\n\n" \
37+"%s\n\n" /* Dynamically built list of builtin protocols */ \
5038 "-v: verbose\n" \
5139 "-V: version\n" \
5240 "-f: foreground\n" \
5341 "-n: numeric output\n" \
42+"-F: use configuration file\n" \
5443 "-t: timeout before connecting to SSH.\n" \
5544 "-p: address and port to listen on.\n Can be used several times to bind to several addresses.\n" \
5645 "--[ssh,ssl,...]: where to connect connections from corresponding protocol.\n" \
57-"-P: PID file. Default: /var/run/sslh.pid.\n" \
46+"-F: specify a configuration file\n" \
47+"-P: PID file.\n" \
5848 "-i: Run as a inetd service.\n" \
5949 "";
6050
61-void print_usage(void)
51+static struct option const_options[] = {
52+ { "inetd", no_argument, &inetd, 1 },
53+ { "foreground", no_argument, &foreground, 1 },
54+ { "numeric", no_argument, &numeric, 1 },
55+ { "verbose", no_argument, &verbose, 1 },
56+ { "user", required_argument, 0, 'u' },
57+ { "config", required_argument, 0, 'F' },
58+ { "pidfile", required_argument, 0, 'P' },
59+ { "timeout", required_argument, 0, 't' },
60+ { "listen", required_argument, 0, 'p' },
61+ {}
62+};
63+static struct option* all_options;
64+static struct proto* builtins;
65+static const char *optstr = "vt:T:p:VP:F:";
66+
67+
68+
69+static void print_usage(void)
6270 {
63- int i;
71+ struct proto *p;
6472 char *prots = "";
6573
66- for (i = 0; i < num_known_protocols; i++)
67- asprintf(&prots, "%s\t[--%s <addr>]\n", prots, protocols[i].description);
74+ for (p = get_first_protocol(); p; p = p->next)
75+ asprintf(&prots, "%s\t[--%s <addr>]\n", prots, p->description);
6876
6977 fprintf(stderr, USAGE_STRING, prots);
7078 }
7179
72-void parse_cmdline(int argc, char* argv[])
80+static void printsettings(void)
7381 {
74- int c, affected = 0;
75- struct option const_options[] = {
76- { "inetd", no_argument, &inetd, 1 },
77- { "foreground", no_argument, &foreground, 1 },
78- { "verbose", no_argument, &verbose, 1 },
79- { "numeric", no_argument, &numeric, 1 },
80- { "user", required_argument, 0, 'u' },
81- { "pidfile", required_argument, 0, 'P' },
82- { "timeout", required_argument, 0, 't' },
83- { "listen", required_argument, 0, 'p' },
84- };
85- struct option all_options[ARRAY_SIZE(const_options) + num_known_protocols + 1];
86- struct addrinfo *addr, **a;
82+ char buf[NI_MAXHOST];
83+ struct addrinfo *a;
84+ struct proto *p;
85+
86+ for (p = get_first_protocol(); p; p = p->next) {
87+ fprintf(stderr,
88+ "%s addr: %s. libwrap service: %s family %d %d\n",
89+ p->description,
90+ sprintaddr(buf, sizeof(buf), p->saddr),
91+ p->service,
92+ p->saddr->ai_family,
93+ p->saddr->ai_addr->sa_family);
94+ }
95+ fprintf(stderr, "listening on:\n");
96+ for (a = addr_listen; a; a = a->ai_next) {
97+ fprintf(stderr, "\t%s\n", sprintaddr(buf, sizeof(buf), a));
98+ }
99+ fprintf(stderr, "timeout to ssh: %d\n", probing_timeout);
100+}
87101
88- memset(all_options, 0, sizeof(all_options));
102+
103+/* Extract configuration on addresses and ports on which to listen.
104+ * out: newly allocated list of addrinfo to listen to
105+ */
106+#ifdef LIBCONFIG
107+static int config_listen(config_t *config, struct addrinfo **listen)
108+{
109+ config_setting_t *setting, *addr;
110+ int len, i;
111+ const char *hostname, *port;
112+
113+ setting = config_lookup(config, "listen");
114+ if (setting) {
115+ len = config_setting_length(setting);
116+ for (i = 0; i < len; i++) {
117+ addr = config_setting_get_elem(setting, i);
118+ if (! (config_setting_lookup_string(addr, "host", &hostname) &&
119+ config_setting_lookup_string(addr, "port", &port))) {
120+ fprintf(stderr,
121+ "line %d:Incomplete specification (hostname and port required)\n",
122+ config_setting_source_line(addr));
123+ return -1;
124+ }
125+
126+ resolve_split_name(listen, hostname, port);
127+
128+ /* getaddrinfo returned a list of addresses corresponding to the
129+ * specification; move the pointer to the end of that list before
130+ * processing the next specification */
131+ for (; *listen; listen = &((*listen)->ai_next));
132+ }
133+ }
134+
135+ return 0;
136+}
137+#endif
138+
139+
140+
141+#ifdef LIBCONFIG
142+static void setup_regex_probe(struct proto *p, config_setting_t* probes)
143+{
144+ int num_probes, errsize, i, res;
145+ char *err;
146+ const char * expr;
147+ regex_t** probe_list;
148+
149+ num_probes = config_setting_length(probes);
150+ if (!num_probes) {
151+ fprintf(stderr, "%s: no probes specified\n", p->description);
152+ exit(1);
153+ }
154+
155+ p->probe = get_probe("regex");
156+ probe_list = calloc(num_probes + 1, sizeof(*probe_list));
157+ p->data = (void*)probe_list;
158+
159+ for (i = 0; i < num_probes; i++) {
160+ probe_list[i] = malloc(sizeof(*(probe_list[i])));
161+ expr = config_setting_get_string_elem(probes, i);
162+ res = regcomp(probe_list[i], expr, 0);
163+ if (res) {
164+ err = malloc(errsize = regerror(res, probe_list[i], NULL, 0));
165+ regerror(res, probe_list[i], err, errsize);
166+ fprintf(stderr, "%s:%s\n", expr, err);
167+ free(err);
168+ exit(1);
169+ }
170+ }
171+}
172+#endif
173+
174+/* Extract configuration for protocols to connect to.
175+ * out: newly-allocated list of protocols
176+ */
177+#ifdef LIBCONFIG
178+static int config_protocols(config_t *config, struct proto **prots)
179+{
180+ config_setting_t *setting, *prot, *probes;
181+ const char *hostname, *port, *name;
182+ int i, num_prots;
183+ struct proto *p, *prev = NULL;
184+
185+ setting = config_lookup(config, "protocols");
186+ if (setting) {
187+ num_prots = config_setting_length(setting);
188+ for (i = 0; i < num_prots; i++) {
189+ p = calloc(1, sizeof(*p));
190+ if (i == 0) *prots = p;
191+ if (prev) prev->next = p;
192+ prev = p;
193+
194+ prot = config_setting_get_elem(setting, i);
195+ if ((config_setting_lookup_string(prot, "name", &name) &&
196+ config_setting_lookup_string(prot, "host", &hostname) &&
197+ config_setting_lookup_string(prot, "port", &port)
198+ )) {
199+ p->description = name;
200+ config_setting_lookup_string(prot, "service", &(p->service));
201+
202+ resolve_split_name(&(p->saddr), hostname, port);
203+
204+
205+ probes = config_setting_get_member(prot, "probe");
206+ if (config_setting_is_array(probes)) {
207+ /* If 'probe' is an array, setup a regex probe using the
208+ * array of strings as pattern */
209+
210+ setup_regex_probe(p, probes);
211+
212+ } else {
213+ /* if 'probe' is 'builtin', set the probe to the
214+ * appropriate builtin protocol */
215+ if (!strcmp(config_setting_get_string(probes), "builtin")) {
216+ p->probe = get_probe(name);
217+ if (!p->probe) {
218+ fprintf(stderr, "%s: no builtin probe for this protocol\n", name);
219+ exit(1);
220+ }
221+ } else {
222+ fprintf(stderr, "%s: illegal probe name\n", name);
223+ exit(1);
224+ }
225+ }
226+ }
227+ }
228+ }
229+
230+ return 0;
231+}
232+#endif
233+
234+/* Parses a config file
235+ * in: *filename
236+ * out: *listen, a newly-allocated linked list of listen addrinfo
237+ * *prots, a newly-allocated linked list of protocols
238+ */
239+#ifdef LIBCONFIG
240+static int config_parse(char *filename, struct addrinfo **listen, struct proto **prots)
241+{
242+ config_t config;
243+ long int timeout;
244+
245+ config_init(&config);
246+ if (config_read_file(&config, filename) == CONFIG_FALSE) {
247+ fprintf(stderr, "%s:%d:%s\n",
248+ filename,
249+ config_error_line(&config),
250+ config_error_text(&config));
251+ exit(1);
252+ }
253+
254+ config_lookup_bool(&config, "verbose", &verbose);
255+ config_lookup_bool(&config, "inetd", &inetd);
256+ config_lookup_bool(&config, "foreground", &foreground);
257+ config_lookup_bool(&config, "numeric", &numeric);
258+
259+ if (config_lookup_int(&config, "timeout", &timeout) == CONFIG_TRUE) {
260+ probing_timeout = timeout;
261+ }
262+
263+ config_lookup_string(&config, "user", &user_name);
264+ config_lookup_string(&config, "pidfile", &pid_file);
265+
266+ config_listen(&config, listen);
267+ config_protocols(&config, prots);
268+
269+ return 0;
270+}
271+#endif
272+
273+/* Adds protocols to the list of options, so command-line parsing uses the
274+ * protocol definition array
275+ * options: array of options to add to; must be big enough
276+ * n_opts: number of options in *options before calling (i.e. where to append)
277+ * prot: array of protocols
278+ * n_prots: number of protocols in *prot
279+ * */
280+static void append_protocols(struct option *options, int n_opts, struct proto *prot , int n_prots)
281+{
282+ int o, p;
283+
284+ for (o = n_opts, p = 0; p < n_prots; o++, p++) {
285+ options[o].name = prot[p].description;
286+ options[o].has_arg = required_argument;
287+ options[o].flag = 0;
288+ options[o].val = p + PROT_SHIFT;
289+ }
290+}
291+
292+static void make_alloptions(void)
293+{
294+ builtins = get_builtins();
295+
296+ /* Create all_options, composed of const_options followed by one option per
297+ * known protocol */
298+ all_options = calloc(ARRAY_SIZE(const_options) + get_num_builtins(), sizeof(struct option));
89299 memcpy(all_options, const_options, sizeof(const_options));
90- append_protocols(all_options, ARRAY_SIZE(const_options), protocols, num_known_protocols);
300+ append_protocols(all_options, ARRAY_SIZE(const_options) - 1, builtins, get_num_builtins());
301+}
91302
92- while ((c = getopt_long_only(argc, argv, "t:T:p:VP:", all_options, NULL)) != -1) {
303+/* Performs a first scan of command line options to see if a configuration file
304+ * is specified. If there is one, parse it now before all other options (so
305+ * configuration file settings can be overridden from the command line).
306+ *
307+ * prots: newly-allocated list of configured protocols, if any.
308+ */
309+static void cmdline_config(int argc, char* argv[], struct proto** prots)
310+{
311+#ifdef LIBCONFIG
312+ int c, res;
313+ char *config_filename;
314+#endif
315+
316+ make_alloptions();
317+
318+#ifdef LIBCONFIG
319+ optind = 1;
320+ opterr = 0; /* we're missing protocol options at this stage so don't output errors */
321+ while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
322+ if (c == 'F') {
323+ config_filename = optarg;
324+ /* find the end of the listen list */
325+ res = config_parse(config_filename, &addr_listen, prots);
326+ if (res)
327+ exit(4);
328+ break;
329+ }
330+ }
331+#endif
332+}
333+
334+
335+/* Parse command-line options. prots points to a list of configured protocols,
336+ * potentially non-allocated */
337+static void parse_cmdline(int argc, char* argv[], struct proto* prots)
338+{
339+ int c;
340+ struct addrinfo **a;
341+ struct proto *p;
342+
343+ optind = 1;
344+ opterr = 1;
345+next_arg:
346+ while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
93347 if (c == 0) continue;
94348
95349 if (c >= PROT_SHIFT) {
96- affected++;
97- protocols[c - PROT_SHIFT].affected = 1;
98- resolve_name(&addr, optarg);
99- protocols[c - PROT_SHIFT].saddr= *addr;
350+ if (prots)
351+ for (p = prots; p && p->next; p = p->next) {
352+ /* override if protocol was already defined by config file
353+ * (note it only overrides address and use builtin probe) */
354+ if (!strcmp(p->description, builtins[c-PROT_SHIFT].description)) {
355+ resolve_name(&(p->saddr), optarg);
356+ p->probe = builtins[c-PROT_SHIFT].probe;
357+ goto next_arg;
358+ }
359+ }
360+ /* At this stage, it's a new protocol: add it to the end of the
361+ * list */
362+ if (!prots) {
363+ /* No protocols yet -- create the list */
364+ p = prots = calloc(1, sizeof(*p));
365+ } else {
366+ p->next = calloc(1, sizeof(*p));
367+ p = p->next;
368+ }
369+ memcpy(p, &builtins[c-PROT_SHIFT], sizeof(*p));
370+ resolve_name(&(p->saddr), optarg);
100371 continue;
101372 }
102373
103374 switch (c) {
104375
376+ case 'F':
377+ /* Legal option, but do nothing, it was already processed in
378+ * cmdline_config() */
379+#ifndef LIBCONFIG
380+ fprintf(stderr, "Built without libconfig support: configuration file not available.\n");
381+ exit(1);
382+#endif
383+ break;
384+
105385 case 't':
106- probing_timeout = atoi(optarg);
386+ probing_timeout = atoi(optarg);
107387 break;
108388
109389 case 'p':
110390 /* find the end of the listen list */
@@ -125,19 +405,25 @@
125405 case 'P':
126406 pid_file = optarg;
127407 break;
128408
409+ case 'v':
410+ verbose++;
411+ break;
412+
129413 default:
130414 print_usage();
131415 exit(2);
132416 }
133417 }
134418
135- if (!affected) {
419+ if (!prots) {
136420 fprintf(stderr, "At least one target protocol must be specified.\n");
137421 exit(2);
138422 }
139423
424+ set_protocol_list(prots);
425+
140426 if (!addr_listen) {
141427 fprintf(stderr, "No listening address specified; use at least one -p option\n");
142428 exit(1);
143429 }
@@ -149,17 +435,18 @@
149435
150436 extern char *optarg;
151437 extern int optind;
152438 int res, num_addr_listen;
439+ struct proto* protocols = NULL;
153440
154441 int *listen_sockets;
155442
156443 /* Init defaults */
157444 pid_file = NULL;
158445 user_name = NULL;
159- foreground = 0;
160446
161- parse_cmdline(argc, argv);
447+ cmdline_config(argc, argv, &protocols);
448+ parse_cmdline(argc, argv, protocols);
162449
163450 if (inetd)
164451 {
165452 verbose = 0;
sslh-select.cView
@@ -1,6 +1,6 @@
11 /*
2- sslh: a SSL/SSH multiplexer
2+ sslh-select: mono-processus server
33
44 # Copyright (C) 2007-2010 Yves Rutschle
55 #
66 # This program is free software; you can redistribute it
@@ -22,8 +22,9 @@
2222
2323 #define __LINUX__
2424
2525 #include "common.h"
26+#include "probe.h"
2627
2728 const char* server_type = "sslh-select";
2829
2930 /* cnx_num_alloc is the number of connection to allocate at once (at start-up,
@@ -115,9 +116,9 @@
115116
116117
117118 /* Connect queue 1 of connection to SSL; returns new file descriptor */
118119 int connect_queue(struct connection *cnx, struct addrinfo *addr,
119- char* cnx_name,
120+ const char* cnx_name,
120121 fd_set *fds_r, fd_set *fds_w)
121122 {
122123 struct queue *q = &cnx->q[1];
123124
@@ -195,9 +196,9 @@
195196 fd_set readfds, writefds; /* working read and write fd sets */
196197 struct timeval tv;
197198 int max_fd, in_socket, i, j, res;
198199 struct connection *cnx;
199- T_PROTO_ID prot;
200+ struct proto *prot;
200201 int num_cnx; /* Number of connections in *cnx */
201202 int num_probing = 0; /* Number of connections currently probing
202203 * We use this to know if we need to time out of
203204 * select() */
@@ -236,9 +237,10 @@
236237 /* Check main socket for new connections */
237238 for (i = 0; i < num_addr_listen; i++) {
238239 if (FD_ISSET(listen_sockets[i], &readfds)) {
239240 in_socket = accept_new_connection(listen_sockets[i], &cnx, &num_cnx);
240- num_probing++;
241+ if (in_socket != -1)
242+ num_probing++;
241243
242244 if (in_socket > 0) {
243245 FD_SET(in_socket, &fds_r);
244246 if (in_socket >= max_fd)
@@ -292,22 +294,22 @@
292294
293295 /* If timed out it's SSH, otherwise the client sent
294296 * data so probe the protocol */
295297 if ((cnx[i].probe_timeout < time(NULL))) {
296- prot = 0;
298+ prot = timeout_protocol();
297299 } else {
298300 prot = probe_client_protocol(&cnx[i]);
299301 }
300302
301303 /* libwrap check if required for this protocol */
302- if (protocols[prot].service &&
303- check_access_rights(in_socket, protocols[prot].service)) {
304+ if (prot->service &&
305+ check_access_rights(in_socket, prot->service)) {
304306 tidy_connection(&cnx[i], &fds_r, &fds_w);
305307 res = -1;
306308 } else {
307309 res = connect_queue(&cnx[i],
308- &protocols[prot].saddr,
309- protocols[prot].description,
310+ prot->saddr,
311+ prot->description,
310312 &fds_r, &fds_w);
311313 }
312314
313315 if (res >= max_fd)
sslh.podView
@@ -5,15 +5,16 @@
55 sslh - ssl/ssh multiplexer
66
77 =head1 SYNOPSIS
88
9-sslh [ B<-t> I<num> ] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<--ssl> I<target address for SSL>] [B<--ssh> I<target address for SSH>] [B<--openvpn> I<target address for OpenVPN>] [B<--http> I<target address for HTTP>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
9+sslh [B<-F> I<config file>] [ B<-t> I<num> ] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<--ssl> I<target address for SSL>] [B<--ssh> I<target address for SSH>] [B<--openvpn> I<target address for OpenVPN>] [B<--http> I<target address for HTTP>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
1010
1111 =head1 DESCRIPTION
1212
13-B<sslh> accepts HTTP, HTTPS, SSH, OpenVPN, tinc and XMPP
14-connections on the same port. This makes it possible to
15-connect to any of these servers on port 443 (e.g. from
13+B<sslh> accepts connections in HTTP, HTTPS, SSH, OpenVPN,
14+tinc, XMPP, or any other protocol that can be tested using a
15+regular expression, on the same port. This makes it possible
16+to connect to any of these servers on port 443 (e.g. from
1617 inside a corporate firewall, which almost never block port
1718 443) while still serving HTTPS on that port.
1819
1920
@@ -37,14 +38,13 @@
3738 client, which is the case of OpenSSH and Putty), or the
3839 client sends its version first ("Bold" client, which is the
3940 case of Bitvise Tunnelier and ConnectBot).
4041
41-B<sslh> waits for some time for the incoming connection to
42-send data. If it stays quiet after the timeout period, it is
43-assumed to be a shy SSH client, and is connected to the SSH
44-server. Otherwise, B<sslh> reads the first packet the client
45-provides, and connects it to the SSH server if it starts
46-with "SSH-", or connects it to the SSL server otherwise.
42+If the client stays quiet after the timeout period, B<sslh>
43+will connect to the first protocol defined (in the
44+configuration file, or on the command line), so SSH should
45+be defined first in B<sslh> configuration to accomodate for
46+shy SSH clients.
4747
4848 =head2 Libwrap support
4949
5050 One drawback of B<sslh> is that the B<ssh> and B<httpd>
@@ -55,15 +55,39 @@
5555 B<libwrap> or B<tcpd>. For this reason, B<sslh> can be
5656 compiled to check SSH accesses against SSH access lists as
5757 defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
5858
59+=head2 Configuration file
60+
61+A configuration file can be supplied to B<sslh>. Command
62+line arguments override file settings. B<sslh> uses
63+B<libconfig> to parse the configuration file, so the general
64+file format is indicated in
65+L<http://www.hyperrealm.com/libconfig/libconfig_manual.html>.
66+Please refer to the example configuration file provided with
67+B<sslh> for the specific format (Options have the same names
68+as on the command line, except for the list of listen ports
69+and the list of protocols).
70+
71+The configuration file makes it possible to specify
72+protocols using regular expressions: a list of regular
73+expressions is given as the I<probe> parameter, and if the
74+first packet received from the client matches any of these
75+expressions, B<sslh> connects to that protocol.
76+
77+Alternatively, the I<probe> parameter can be set to
78+"builtin", to use the compiled probes which are much faster
79+than regular expressions.
80+
81+
5982 =head1 OPTIONS
6083
6184 =over 4
6285
6386 =item B<-t> I<num>, B<--timeout> I<num>
6487
65-Timeout before a connection is considered to be SSH. Default
88+Timeout before forwarding the connection to the first
89+configured protocol (which should usually be SSH). Default
6690 is 2s.
6791
6892 =item B<-p> I<listening address>, B<--listen> I<listening address>
6993
@@ -124,9 +148,9 @@
124148 =item B<-u> I<username>, B<--user> I<username>
125149
126150 Requires to run under the specified username.
127151
128-=item B<-P> I<pidfile>, B<--pid-file> I<pidfile>
152+=item B<-P> I<pidfile>, B<--pidfile> I<pidfile>
129153
130154 Specifies a file in which to write the PID of the main
131155 server.
132156
@@ -164,9 +188,9 @@
164188 =head1 SEE ALSO
165189
166190 Last version available from
167191 L<http://www.rutschle.net/tech/sslh>, and can be tracked
168-from L<http://freshmeat.net/projects/sslh/>.
192+from L<http://freecode.com/projects/sslh>.
169193
170194 =head1 AUTHOR
171195
172196 Written by Yves Rutschle
tView
@@ -54,9 +54,11 @@
5454 # Start sslh with the right plumbing
5555 my $sslh_pid;
5656 if (!($sslh_pid = fork)) {
5757 my $user = (getpwuid $<)[0]; # Run under current username
58- exec "./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
58+ my $cmd = "./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
59+ warn "$cmd\n";
60+ exec $cmd;
5961 #exec "valgrind --leak-check=full ./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address -ssl $ssl_address -P $pidfile";
6062 exit 0;
6163 }
6264 warn "spawned $sslh_pid\n";
t_loadView
@@ -26,9 +26,9 @@
2626 # Delay between starting new processes when starting up. If
2727 # you start 200 processes in under a second, things go wrong
2828 # and it's not sslh's fault (typically the echosrv won't be
2929 # forking fast enough).
30-my $start_time_delay = 0.5;
30+my $start_time_delay = 1;
3131
3232 # If you test 4 protocols, you'll start $NUM_CNX * 4 clients
3333 # (e.g. 40), starting one every $start_time_delay seconds.
3434
example.cfgView
@@ -1,0 +1,40 @@
1+verbose: false;
2+foreground: true;
3+inetd: false;
4+numeric: false;
5+timeout: 2;
6+user: "nobody";
7+pidfile: "/var/run/sslh.pid";
8+
9+
10+# List of interfaces on which we should listen
11+listen:
12+(
13+ { host: "thelonious"; port: "443"; }
14+# , { host: "thelonious"; port: "8080"; }
15+);
16+
17+# List of protocols
18+#
19+# Each protocol entry consists of:
20+# name: name of the protocol
21+# service: (optional) libwrap service name (see hosts_access(5))
22+# host: host name to connect that protocol
23+# port: port number to connect that protocol
24+# probe: "builtin" or a list of regular expressions
25+#
26+# In case of timeout sslh will connect to the first
27+# protocol: this should be SSH.
28+# SSL should have a "always true" probe, and come last.
29+# sslh will try each probe in order they are declared, and
30+# connect to the first that matches.
31+
32+protocols:
33+(
34+ { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
35+ { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
36+ { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; },
37+ { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
38+ { name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; }
39+);
40+
probe.cView
@@ -1,0 +1,216 @@
1+/*
2+# probe.c: Code for probing protocols
3+#
4+# Copyright (C) 2007-2012 Yves Rutschle
5+#
6+# This program is free software; you can redistribute it
7+# and/or modify it under the terms of the GNU General Public
8+# License as published by the Free Software Foundation; either
9+# version 2 of the License, or (at your option) any later
10+# version.
11+#
12+# This program is distributed in the hope that it will be
13+# useful, but WITHOUT ANY WARRANTY; without even the implied
14+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15+# PURPOSE. See the GNU General Public License for more
16+# details.
17+#
18+# The full text for the General Public License is here:
19+# http://www.gnu.org/licenses/gpl.html
20+*/
21+
22+#include <regex.h>
23+#include "probe.h"
24+
25+
26+
27+static int is_ssh_protocol(const char *p, int len, struct proto*);
28+static int is_openvpn_protocol(const char *p, int len, struct proto*);
29+static int is_tinc_protocol(const char *p, int len, struct proto*);
30+static int is_xmpp_protocol(const char *p, int len, struct proto*);
31+static int is_http_protocol(const char *p, int len, struct proto*);
32+static int is_true(const char *p, int len, struct proto* proto) { return 1; }
33+
34+/* Table of protocols that have a built-in probe
35+ */
36+static struct proto builtins[] = {
37+ /* description service saddr probe */
38+ { "ssh", "sshd", NULL, is_ssh_protocol},
39+ { "openvpn", NULL, NULL, is_openvpn_protocol },
40+ { "tinc", NULL, NULL, is_tinc_protocol },
41+ { "xmpp", NULL, NULL, is_xmpp_protocol },
42+ { "http", NULL, NULL, is_http_protocol },
43+ { "ssl", NULL, NULL, is_true }
44+};
45+
46+static struct proto *protocols;
47+
48+struct proto* get_builtins(void) {
49+ return builtins;
50+}
51+
52+int get_num_builtins(void) {
53+ return ARRAY_SIZE(builtins);
54+}
55+
56+/* Returns the protocol to connect to in case of timeout; conventionaly this is
57+ * the first protocol specified (but maybe we'll make it more explicit some
58+ * day)
59+ */
60+struct proto* timeout_protocol(void) {
61+ return protocols;
62+}
63+
64+/* returns the first protocol (caller can then follow the *next pointers) */
65+struct proto* get_first_protocol(void)
66+{
67+ return protocols;
68+}
69+
70+void set_protocol_list(struct proto* prots)
71+{
72+ protocols = prots;
73+}
74+
75+/* Is the buffer the beginning of an SSH connection? */
76+static int is_ssh_protocol(const char *p, int len, struct proto *proto)
77+{
78+ if (!strncmp(p, "SSH-", 4)) {
79+ return 1;
80+ }
81+ return 0;
82+}
83+
84+/* Is the buffer the beginning of an OpenVPN connection?
85+ * (code lifted from OpenVPN port-share option)
86+ */
87+static int is_openvpn_protocol (const char*p,int len, struct proto *proto)
88+{
89+#define P_OPCODE_SHIFT 3
90+#define P_CONTROL_HARD_RESET_CLIENT_V2 7
91+ if (len >= 3)
92+ {
93+ return p[0] == 0
94+ && p[1] >= 14
95+ && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
96+ }
97+ else if (len >= 2)
98+ {
99+ return p[0] == 0 && p[1] >= 14;
100+ }
101+ else
102+ return 0;
103+}
104+
105+/* Is the buffer the beginning of a tinc connections?
106+ * (protocol is undocumented, but starts with "0 " in 1.0.15)
107+ * */
108+static int is_tinc_protocol( const char *p, int len, struct proto *proto)
109+{
110+ return !strncmp(p, "0 ", 2);
111+}
112+
113+/* Is the buffer the beginning of a jabber (XMPP) connections?
114+ * (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
115+ * clients, just checking first frame containing "jabber" in xml entity)
116+ * */
117+static int is_xmpp_protocol( const char *p, int len, struct proto *proto)
118+{
119+ return strstr(p, "jabber") ? 1 : 0;
120+}
121+
122+static int probe_http_method(const char *p, const char *opt)
123+{
124+ return !strcmp(p, opt);
125+}
126+
127+/* Is the buffer the beginning of an HTTP connection? */
128+static int is_http_protocol(const char *p, int len, struct proto *proto)
129+{
130+ /* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
131+ if (strstr(p, "HTTP"))
132+ return 1;
133+
134+ /* Otherwise it could be HTTP/1.0 without version: check if it's got an
135+ * HTTP method (RFC2616 5.1.1) */
136+ probe_http_method(p, "OPTIONS");
137+ probe_http_method(p, "GET");
138+ probe_http_method(p, "HEAD");
139+ probe_http_method(p, "POST");
140+ probe_http_method(p, "PUT");
141+ probe_http_method(p, "DELETE");
142+ probe_http_method(p, "TRACE");
143+ probe_http_method(p, "CONNECT");
144+
145+ return 0;
146+}
147+
148+
149+static int regex_probe(const char *p, int len, struct proto *proto)
150+{
151+ regex_t** probe_list = (regex_t**)(proto->data);
152+ int i=0;
153+
154+ while (probe_list[i]) {
155+ if (!regexec(probe_list[i], p, 0, NULL, 0)) {
156+ return 1;
157+ }
158+ i++;
159+ }
160+ return 0;
161+}
162+
163+/*
164+ * Read the beginning of data coming from the client connection and check if
165+ * it's a known protocol. Then leave the data on the defered
166+ * write buffer of the connection and returns a pointer to the protocol
167+ * structure
168+ */
169+struct proto* probe_client_protocol(struct connection *cnx)
170+{
171+ char buffer[BUFSIZ];
172+ struct proto *p;
173+ int n;
174+
175+ n = read(cnx->q[0].fd, buffer, sizeof(buffer));
176+ /* It's possible that read() returns an error, e.g. if the client
177+ * disconnected between the previous call to select() and now. If that
178+ * happens, we just connect to the default protocol so the caller of this
179+ * function does not have to deal with a specific failure condition (the
180+ * connection will just fail later normally). */
181+ if (n > 0) {
182+ defer_write(&cnx->q[1], buffer, n);
183+
184+ for (p = protocols; p; p = p->next) {
185+ if (p->probe(buffer, n, p)) {
186+ return p;
187+ }
188+ }
189+ }
190+
191+ /* If none worked, return the first one affected (that's completely
192+ * arbitrary) */
193+ return protocols;
194+}
195+
196+/* Returns the probe for specified protocol:
197+ * parameter is the description in builtins[], or "regex"
198+ * */
199+T_PROBE* get_probe(const char* description) {
200+ int i;
201+
202+ for (i = 0; i < ARRAY_SIZE(builtins); i++) {
203+ if (!strcmp(builtins[i].description, description)) {
204+ return builtins[i].probe;
205+ }
206+ }
207+ /* Special case of "regex" probe (we don't want to set it in builtins
208+ * because builtins is also used to build the command-line options and
209+ * regexp is not legal on the command line)*/
210+ if (!strcmp(description, "regex"))
211+ return regex_probe;
212+
213+ return NULL;
214+}
215+
216+
probe.hView
@@ -1,0 +1,54 @@
1+/* API for probe.c */
2+
3+#ifndef __PROBE_H_
4+#define __PROBE_H_
5+
6+#include "common.h"
7+
8+struct proto;
9+typedef int T_PROBE(const char*, int, struct proto*);
10+
11+/* For each protocol we need: */
12+struct proto {
13+ const char* description; /* a string that says what it is (for logging and command-line parsing) */
14+ const char* service; /* service name to do libwrap checks */
15+ struct addrinfo *saddr; /* list of addresses to try and switch that protocol */
16+
17+ /* function to probe that protocol; parameters are buffer and length
18+ * containing the data to probe, and a pointer to the protocol structure */
19+ T_PROBE* probe;
20+ void* data; /* opaque pointer ; used to pass list of regex to regex probe */
21+ struct proto *next; /* pointer to next protocol in list, NULL if last */
22+};
23+
24+/* Returns a pointer to the array of builtin protocols */
25+struct proto * get_builtins(void);
26+
27+/* Returns the number of builtin protocols */
28+int get_num_builtins(void);
29+
30+/* Returns the probe for specified protocol */
31+T_PROBE* get_probe(const char* description);
32+
33+/* Returns the head of the configured protocols */
34+struct proto* get_first_protocol(void);
35+
36+/* Set the list of configured protocols */
37+void set_protocol_list(struct proto*);
38+
39+/* probe_client_protocol
40+ *
41+ * Read the beginning of data coming from the client connection and check if
42+ * it's a known protocol. Then leave the data on the defered
43+ * write buffer of the connection and returns a pointer to the protocol
44+ * structure
45+ */
46+struct proto* probe_client_protocol(struct connection *cnx);
47+
48+/* timeout_protocol
49+ *
50+ * Returns the protocol to connect to in case of timeout
51+ */
52+struct proto* timeout_protocol(void);
53+
54+#endif

Built with git-ssb-web