diff --git a/auth.h b/auth.h
index df8ae0ce96828d48885b50a007ca23f2a811c726..106a1ad058e2ee35e3bde99f77b63fff10b44633 100644
--- a/auth.h
+++ b/auth.h
@@ -47,6 +47,7 @@ void recv_msg_userauth_success();
 void cli_get_user();
 void cli_auth_getmethods();
 void cli_auth_try();
+void recv_msg_userauth_banner();
 
 
 #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
diff --git a/cli-auth.c b/cli-auth.c
index e08158729c002aa5df7ca9454ebf2e83c54964e1..549349e70ae6fbbc14f64569d3014d729a8760d6 100644
--- a/cli-auth.c
+++ b/cli-auth.c
@@ -35,6 +35,48 @@ void cli_auth_getmethods() {
 
 }
 
+void recv_msg_userauth_banner() {
+
+	unsigned char* banner = NULL;
+	unsigned int bannerlen;
+	unsigned int i, linecount;
+
+	TRACE(("enter recv_msg_userauth_banner"));
+	if (ses.authstate.authdone) {
+		TRACE(("leave recv_msg_userauth_banner: banner after auth done"));
+		return;
+	}
+
+	banner = buf_getstring(ses.payload, &bannerlen);
+	buf_eatstring(ses.payload); /* The language string */
+
+	if (bannerlen > MAX_BANNER_SIZE) {
+		TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen));
+		goto out;
+	}
+
+	cleantext(banner);
+
+	/* Limit to 25 lines */
+	linecount = 1;
+	for (i = 0; i < bannerlen; i++) {
+		if (banner[i] == '\n') {
+			if (linecount >= MAX_BANNER_LINES) {
+				banner[i] = '\0';
+				break;
+			}
+			linecount++;
+		}
+	}
+
+	printf("%s\n", banner);
+
+out:
+	m_free(banner);
+	TRACE(("leave recv_msg_userauth_banner"));
+}
+
+
 void recv_msg_userauth_failure() {
 
 	unsigned char * methods = NULL;
diff --git a/cli-session.c b/cli-session.c
index 6882d2e38952dfcdf2495ed7f07405fc41972933..e8c6ae6cb9d46dc29b9a046bd194524a99b54c07 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -36,6 +36,7 @@ static const packettype cli_packettypes[] = {
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
 	{SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, // client
 	{SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, // client
+	{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, // client
 	{0, 0} /* End */
 };
 
@@ -217,3 +218,24 @@ static void cli_remoteclosed() {
 	ses.sock = -1;
 	dropbear_exit("remote closed the connection");
 }
+
+/* Operates in-place turning dirty (untrusted potentially containing control
+ * characters) text into clean text. */
+void cleantext(unsigned char* dirtytext) {
+
+	unsigned int i, j;
+	unsigned char c, lastchar;
+
+	j = 0;
+	for (i = 0; dirtytext[i] != '\0'; i++) {
+
+		c = dirtytext[i];
+		/* We can ignore '\r's */
+		if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
+			dirtytext[j] = c;
+			j++;
+		}
+	}
+	/* Null terminate */
+	dirtytext[j] = '\0';
+}
diff --git a/options.h b/options.h
index 1ab16c72cb2d1e1d30ed76f8c103f4a235bd53a4..8ed21aba624332f1b361fb7630332fec92ccd493 100644
--- a/options.h
+++ b/options.h
@@ -195,6 +195,7 @@
 #endif
 
 #define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
+#define MAX_BANNER_LINES 20 /* How many lines the client will display */
 
 #define DEV_URANDOM "/dev/urandom"
 
diff --git a/session.h b/session.h
index 2009054492635b5210ed9cc7c0af549cbbfd8791..23233770b07aa9241426b685202a6919fcf76945 100644
--- a/session.h
+++ b/session.h
@@ -56,6 +56,7 @@ void cli_session(int sock, char *remotehost);
 void cli_dropbear_exit(int exitcode, const char* format, va_list param);
 void cli_dropbear_log(int priority, const char* format, va_list param);
 void cli_session_cleanup();
+void cleantext(unsigned char* dirtytext);
 
 struct key_context {