sshd also talking HTTP

Dan Kaminsky dan at doxpara.com
Fri Jul 11 00:37:51 EST 2003


Jan--

    Your hack is useful, but (being a bit more diplomatic than Damien 
*smiles*) indeed should be generalized into an external application.  
Rather than launching separate instances of SSHD, however, you may 
simply use the delay to select your port forward destination.  So, your 
app listens on 80; if any bytes are read from the 
client(GET/HEAD/POST/TRACE/etc) you forward to a web server running 
elsewhere, and if no bytes are read, you forward to 22.  Though 
trickier, you can actually fool the server into believing it had the 
original socket (thus getting the correct IP in your logs); see the 
stunnel source to see how this is done.  It may be Linux specific, 
though.  Anyway, this prevents loss of entropy.

    The described tactic should also work with SSL/443, which also gets 
through firewalls well.  And, of course, there's httptunnel, which can 
be used as an ssh transport via ProxyCommand.

    C'mon, Damien :-)  The very use of crypto is predicated on the fact 
that networks are imperfect.  Some people have more horrifyingly 
imperfect network than others.  And besides, how much scoffing at do we 
get from the IPSec boosters?

--Dan
www.doxpara.com


Jan Pieter Cornet wrote:

>(I'm not subscribed to the list, please Cc me on replies).
>
>We have configured sshd to listen on port 80 for some of our users who
>are behind sufficiently paranoid firewalls. However, others are now
>confused since they're expecting a web server on port 80.
>
>So, I created a small patch (just as proof-of-concept so far), that
>determines the type of client connecting. A web client will start talking
>itself (GET, HEAD, etc...), while an ssh client will wait for the server
>to issue the greeting banner.
>
>So, the patch simply waits 1 second (should be configurable) when someone
>connects to port 80 (should also be configurable), and if any data is
>available by then, it decides it's an HTTP client, not an SSH client,
>and sends a proper redirect.
>
>The patch is attached (or in case the attachment gets stripped, also here:
>http://www.xs4all.nl/~johnpc/dirty-sshd-hack.txt )
>
>Could a cleaned up version of this patch be useful for inclusion in future
>versions of Opensshd? Note that it is specifically _not_ the idea to put
>a full-blown HTTP server inside sshd, only enough to redirect to another
>URL (which should of course be configurable).
>
>Another way to solve the same problem would be to run sshd in "inetd"
>mode, behind a simple wrapper script that does the HTTP detection and
>redirection. However, that has the inherent disadvantages of the inetd
>mode (like wasting entropy).
>
>I'm willing to invest some time in making the patch suitable, if it is
>decided that it could be useful.
>
>  
>
>------------------------------------------------------------------------
>
>--- sshd.c.orig	Mon Mar 10 01:38:10 2003
>+++ sshd.c	Wed May 14 02:47:07 2003
>@@ -52,6 +52,11 @@
> #include <sys/security.h>
> #include <prot.h>
> #endif
>+#ifdef DOUBLE_AS_HTTPD
>+#include <sys/select.h>
>+#include <sys/time.h>
>+#include <stdio.h>
>+#endif
> 
> #include "ssh.h"
> #include "ssh1.h"
>@@ -483,6 +488,152 @@
> 	}
> }
> 
>+#ifdef DOUBLE_AS_HTTPD
>+static void sshd_act_like_an_httpd(int sock_in, int sock_out);
>+static void sshd_httpd_timeout(int sig);
>+
>+/* intercept httpd */
>+static void
>+sshd_intercept_possible_httpd(int sock_in, int sock_out)
>+{
>+	struct sockaddr local;
>+	int local_len;
>+	fd_set readfds;
>+	struct timeval onesec;
>+
>+	local_len = sizeof(local);
>+	if ( getsockname(sock_in, &local, &local_len) != 0 ) {
>+		log("HTTPD HACK: getsockname failed: %.100s",
>+		    strerror(errno));
>+		return;
>+	}
>+	if ( local.sa_family != AF_INET ) {
>+		log("HTTPD HACK: strange sock_in.sa_family: %d",
>+			local.sa_family);
>+		return;
>+	}
>+	if ( ntohs(((struct sockaddr_in*) &local)->sin_port) != 80 ) {
>+		/* XXX this logging should be removed */
>+		log("HTTPD HACK: incoming port not 80 but %d",
>+			ntohs(((struct sockaddr_in*) &local)->sin_port));
>+		return;
>+	}
>+
>+	/* wait 1 second for a valid http request coming in */
>+	FD_ZERO(&readfds);
>+	FD_SET(sock_in, &readfds);
>+	onesec.tv_sec = 1;
>+	onesec.tv_usec = 0;
>+	if ( ! select(sock_in + 1, &readfds, NULL, NULL, &onesec) ) {
>+		log("HTTPD HACK: nothing incoming for 1 second");
>+		return;
>+	}
>+	if ( ! FD_ISSET(sock_in, &readfds) ) {
>+		log("HTTPD HACK: hm, sock_in not readable");
>+		return;
>+	}
>+
>+	/*
>+	 * Something is in the buffer right now. This is not an ssh client.
>+	 * so from here on, we will never return to the real program,
>+	 * and assume it is an HTTP request.
>+	 */
>+
>+	sshd_act_like_an_httpd(sock_in, sock_out);
>+	exit(0);
>+}
>+
>+static void
>+sshd_act_like_an_httpd(int sock_in, int sock_out)
>+{
>+	FILE* in;
>+	char httpreq[1024];
>+	char hdrline[1024];
>+	char outbuf[4096];
>+	char* p;
>+	char* url;
>+
>+	/* setup an alarm call to abort playing HTTPD reasonably soon. */
>+	signal(SIGALRM, sshd_httpd_timeout);
>+	if (!debug_flag)
>+		alarm(60);
>+
>+	if ( !(in = fdopen(sock_in, "r+")) ) {
>+		log("HTTPD HACK: fdopen failed: %.100s", strerror(errno));
>+		return;
>+	}
>+
>+	/* read in the first line of the request */
>+	if ( ! fgets(httpreq, sizeof(httpreq), in) ) {
>+		log("HTTPD HACK: fgets failed on first line: %.100s",
>+			strerror(errno));
>+		return;
>+	}
>+
>+	/* must be a GET request... for NOW. Support more */
>+	if ( strncmp(httpreq, "GET ", 4) ) {
>+		log("HTTPD HACK: no GET request, but a %.100s", httpreq);
>+		return;
>+	}
>+	url = httpreq + 4;
>+
>+	if ( !(p = strchr(url, ' ')) ) {
>+		log("HTTPD HACK: HTTP/0.9 request: %.100s", httpreq);
>+		return;
>+	}
>+
>+	*p++ = '\0';
>+	/* must be a HTTP/1.x request */
>+	if ( strncmp(p, "HTTP/1.", 7) ) {
>+		log("HTTPD HACK: Not a HTTP/1.x request but %.100s", p);
>+		return;
>+	}
>+
>+	log("HTTPD HACK: faking a request for GET %.100s", url);
>+
>+	/* read (and ignore) the subsequent header */
>+	strcpy(hdrline, "foo");
>+	while ( strlen(hdrline) > 0 ) {
>+		if ( ! fgets(hdrline, sizeof(hdrline), in) ) {
>+			log("HTTPD HACK: fgets failed on header: %.100s",
>+				strerror(errno));
>+			return;
>+		}
>+		/* strip CR+LF */
>+		if ( (p = strchr(hdrline, '\r')) != NULL )
>+			*p = '\0';
>+		if ( (p = strchr(hdrline, '\n')) != NULL )
>+			*p = '\0';
>+		log("HTTPD HACK: ignoring header %.100s", hdrline);
>+	}
>+
>+	/* output the redirect. To a fixed site for proof of concept. */
>+	snprintf(outbuf, sizeof(outbuf), "\
>+HTTP/1.0 301 Don't be so lazy and type that www\r\n\
>+Server: sshd %.100s\r\n\
>+Location: http://www.xs4all.nl%s\r\n\
>+Connection: close\r\n\
>+Content-Type: text/plain\r\n\
>+\r\n\
>+Don't be so lazy, and simply type www.xs4all.nl instead of just xs4all.nl\r\n\
>+",
>+		SSH_VERSION,
>+		url
>+	    );
>+	write(sock_out, &outbuf, strlen(outbuf));
>+	return;
>+}
>+
>+static void
>+sshd_httpd_timeout(int sig)
>+{
>+	/* Log error and exit. */
>+	fatal("Timeout in acting like HTTPD for connection from %s",
>+	    get_remote_ipaddr());
>+}
>+
>+#endif
>+
> /* Destroy the host and server keys.  They will no longer be needed. */
> void
> destroy_sensitive_data(void)
>@@ -1457,6 +1608,14 @@
> 
> 	/* Log the connection. */
> 	verbose("Connection from %.500s port %d", remote_ip, remote_port);
>+
>+#ifdef DOUBLE_AS_HTTPD
>+	/*
>+	 * Wait a few instants to see if an HTTP request comes in,
>+	 * then handle that. This is only done for port 80.
>+	 */
>+	sshd_intercept_possible_httpd(sock_in, sock_out);
>+#endif
> 
> 	/*
> 	 * We don\'t want to listen forever unless the other side
>  
>
>------------------------------------------------------------------------
>
>_______________________________________________
>openssh-unix-dev mailing list
>openssh-unix-dev at mindrot.org
>http://www.mindrot.org/mailman/listinfo/openssh-unix-dev
>  
>





More information about the openssh-unix-dev mailing list