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!