yrutschle / sslh

Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)

Home Page:https://www.rutschle.net/tech/sslh/README.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Probe length is null on probe_buffer when using inetd mode using parameter

ebrahimkarimi opened this issue · comments

i followed the documents to setup stunnel and sslh for nginx and ssh and after a day of trying and facing different problems(sometimes openning ssh on the browser!!!!) i decided to debug it and found out:


/* Run all the probes on a buffer
 * buf, len: buffer to test on
 * proto_in, proto_len: array of protocols to try
 * proto_out: protocol that matched
 *
 * Returns
 *      PROBE_AGAIN if not enough data, and set *proto to NULL
 *      PROBE_MATCH if protocol is identified, in which case *proto is set to
 *      point to the appropriate protocol
 * */
int probe_buffer(char* buf, int len,
                 struct sslhcfg_protocols_item** proto_in,
                 int proto_len,
                 struct sslhcfg_protocols_item** proto_out
                 )
{
    struct sslhcfg_protocols_item* p;
    int i, res, again = 0;

    print_message(msg_packets, "hexdump of incoming packet:\n");
    hexdump(msg_packets, buf, len);

   // HERE =============
    print_message(msg_probe_info,"Proto len is %d\n",proto_len);
   // ===================
    *proto_out = NULL;
    for (i = 0; i < proto_len; i++) {
        char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
        p = proto_in[i];

        if (! p->probe) continue;

        print_message(msg_probe_info, "probing for %s\n", p->name);

        /* Don't probe last protocol if it is anyprot (and store last protocol) */
        if ((i == proto_len - 1) && (!strcmp(p->name, "anyprot")))
            break;

        if (p->minlength_is_present && (len < p->minlength )) {
            print_message(msg_probe_info, "input too short, %d bytes but need %d\n", 
                          len , p->minlength);
            again++;
            continue;
        }

        res = p->probe(buf, len, p);
        print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);

        if (res == PROBE_MATCH) {
            *proto_out = p;
            return PROBE_MATCH;
        }
        if (res == PROBE_AGAIN)
            again++;
    }
    if (again)
        return PROBE_AGAIN;

    /* Everything failed: match the last one */

    if (proto_len == 0) {
        /* This should be caught by configuration sanity checks, but just in
         * case, die gracefully rather than segfaulting */
        print_message(msg_int_error, "Received traffic on transport that has no target\n");
        exit(0);
    }
    *proto_out = proto_in[proto_len-1];
    return PROBE_MATCH;
}

proto_len is 0 !!!!

the stunnel config is :

exec = /usr/sbin/sslh
execArgs =  sslh -i --verbose-probe-info=2 --verbose-connections=2 --verbose-connections-error=2 --http localhost:1443 --ssh localhost:22

sslh -V output is:
sslh-fork v2.0-rc2-70-g4cc0867-dirty

This is odd, it should be caught by tcp_probe.c:tcp_sanity_check(). Also, you have two TCP targets so it should be 2.

Can you add some debugging in tcp_probe.c to check that tcp_sanity_check it called?

I've been enjoying sslh for a while. Thanks! I ran into this bug when Gentoo stablized sslh 2.1.1 and I upgraded from 1.22c.

The problem is that in inetd mode main() calls start_shoveler() without calling tcp_init() first to initialize tcp_protocols_len. Then start_shoveler() calls probe_client_protocol() which passes tcp_protocols_len=0 to probe_buffer() which makes it skip all protocol matching. It's not a problem in non-inetd mode because there main() instead calls main_loop() which does call tcp_init() first.

Here's a minimal patch to get inetd mode working again. It applies and fixes the problem on current master and should do as well previous versions going back a couple of years.
sslh-2.1.1_tcp_init.patch

This patch would result in a memory leak for non-inetd mode (it calls tcp_init() each time a new connection is probed, which results in new memory allocation for configuration.
I would rather simply call it in sslh-main.c before calling start_shoveler():

diff --git a/sslh-main.c b/sslh-main.c
index b427dac..942268f 100644
--- a/sslh-main.c
+++ b/sslh-main.c
@@ -246,6 +246,7 @@ int main(int argc, char *argv[], char* envp[])
    if (cfg.inetd)
    {
        close(fileno(stderr)); /* Make sure no error will go to client */
+       tcp_init();
        start_shoveler(0);
        exit(0);
    }

can you confirm it works with inetd? (I don't use it myself...) I can make a patch release following.

Thanks for the investigation, anyhow!

My patch does not result in a memory leak because tcp_protocols_len>0 if tcp_init() has already been called and memory allocated. That's why I put that check around the tcp_init() call. I chose to make the change in tcp-probe.c because tcp_protocols_len and tcp_init() are in that same file, so it's easier to follow and understand. Looking at it again, it might be nice to also add a comment saying why tcp_init() might need to be called there.

That said, I just verified that your tcp_init() call in sslh_main.c also works and it makes sense to put it in the inetd-specific code. You'll probably also want to include tcp-probe.h up top to avoid an "implicit declaration" warning for tcp_init().

Whichever approach you like is fine with me. Thanks for your hard work.

of course you're right, I followed the calltree and by the time I was at the memory allocation I'd forgotten where I was coming from :-)

I've pushed the patch, and published a 2.1.2 version with the fix.

again thanks for the investigation!