From 858252fb1d451ebb0969cf9749116c8f0ee42753 Mon Sep 17 00:00:00 2001
From: "dtucker@openbsd.org" <dtucker@openbsd.org>
Date: Wed, 1 Feb 2017 02:59:09 +0000
Subject: upstream commit

Return true reason for port forwarding failures where
feasible rather than always "administratively prohibited".  bz#2674, ok djm@

Upstream-ID: d901d9887951774e604ca970e1827afaaef9e419
---
 channels.c   | 33 ++++++++++++++++++++++++++-------
 channels.h   |  5 +++--
 serverloop.c | 17 ++++++++++-------
 3 files changed, 39 insertions(+), 16 deletions(-)

Index: openssh-6.6p1/channels.c
===================================================================
--- openssh-6.6p1.orig/channels.c	2019-04-09 14:02:34.980087253 +0200
+++ openssh-6.6p1/channels.c	2019-04-09 14:34:06.759040349 +0200
@@ -2654,7 +2654,7 @@ channel_input_port_open(int type, u_int3
 	}
 	packet_check_eom();
 	c = channel_connect_to(host, host_port,
-	    "connected socket", originator_string);
+	    "connected socket", originator_string, NULL, NULL);
 	free(originator_string);
 	free(host);
 	if (c == NULL) {
@@ -3326,9 +3326,13 @@ channel_connect_ctx_free(struct channel_
 	memset(cctx, 0, sizeof(*cctx));
 }
 
-/* Return CONNECTING channel to remote host, port */
+/*
+ * Return CONNECTING channel to remote host:port,
+ * passing back the failure reason if appropriate.
+ */
 static Channel *
-connect_to(const char *host, u_short port, char *ctype, char *rname)
+connect_to_reason(const char *name, int port, char *ctype, char *rname,
+     int *reason, const char **errmsg)
 {
 	struct addrinfo hints;
 	int gaierr;
@@ -3342,19 +3346,23 @@ connect_to(const char *host, u_short por
 	hints.ai_family = IPv4or6;
 	hints.ai_socktype = SOCK_STREAM;
 	snprintf(strport, sizeof strport, "%d", port);
-	if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
-		error("connect_to %.100s: unknown host (%s)", host,
+	if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) {
+		if (errmsg != NULL)
+			*errmsg = ssh_gai_strerror(gaierr);
+		if (reason != NULL)
+			*reason = SSH2_OPEN_CONNECT_FAILED;
+		error("connect_to %.100s: unknown host (%s)", name,
 		    ssh_gai_strerror(gaierr));
 		return NULL;
 	}
 
-	cctx.host = xstrdup(host);
+	cctx.host = xstrdup(name);
 	cctx.port = port;
 	cctx.ai = cctx.aitop;
 
 	if ((sock = connect_next(&cctx)) == -1) {
 		error("connect to %.100s port %d failed: %s",
-		    host, port, strerror(errno));
+		    name, port, strerror(errno));
 		channel_connect_ctx_free(&cctx);
 		return NULL;
 	}
@@ -3364,6 +3372,13 @@ connect_to(const char *host, u_short por
 	return c;
 }
 
+/* Return CONNECTING channel to remote host:port or local socket path */
+static Channel *
+connect_to(const char *name, int port, char *ctype, char *rname)
+{
+	return connect_to_reason(name, port, ctype, rname, NULL, NULL);
+}
+
 Channel *
 channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
 {
@@ -3384,7 +3399,8 @@ channel_connect_by_listen_address(u_shor
 
 /* Check if connecting to that port is permitted and connect. */
 Channel *
-channel_connect_to(const char *host, u_short port, char *ctype, char *rname)
+channel_connect_to(const char *host, u_short port, char *ctype,
+    char *rname, int *reason, const char **errmsg)
 {
 	int i, permit, permit_adm = 1;
 
@@ -3410,9 +3426,11 @@ channel_connect_to(const char *host, u_s
 	if (!permit || !permit_adm) {
 		logit("Received request to connect to host %.100s port %d, "
 		    "but the request was denied.", host, port);
+		if (reason != NULL)
+			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
 		return NULL;
 	}
-	return connect_to(host, port, ctype, rname);
+	return connect_to_reason(host, port, ctype, rname, reason, errmsg);
 }
 
 void
Index: openssh-6.6p1/channels.h
===================================================================
--- openssh-6.6p1.orig/channels.h	2019-04-09 14:02:34.980087253 +0200
+++ openssh-6.6p1/channels.h	2019-04-09 14:03:04.072212704 +0200
@@ -264,7 +264,8 @@ void	 channel_clear_permitted_opens(void
 void	 channel_clear_adm_permitted_opens(void);
 void 	 channel_print_adm_permitted_opens(void);
 int      channel_input_port_forward_request(int, int);
-Channel	*channel_connect_to(const char *, u_short, char *, char *);
+Channel	*channel_connect_to(const char *, u_short, char *, char *, int *,
+	     const char **);
 Channel	*channel_connect_stdio_fwd(const char*, u_short, int, int);
 Channel	*channel_connect_by_listen_address(u_short, char *, char *);
 int	 channel_request_remote_forwarding(const char *, u_short,
Index: openssh-6.6p1/serverloop.c
===================================================================
--- openssh-6.6p1.orig/serverloop.c	2019-04-09 14:02:34.980087253 +0200
+++ openssh-6.6p1/serverloop.c	2019-04-09 14:03:04.072212704 +0200
@@ -952,7 +952,7 @@ server_input_window_size(int type, u_int
 }
 
 static Channel *
-server_request_direct_tcpip(void)
+server_request_direct_tcpip(int *reason, const char **errmsg)
 {
 	Channel *c = NULL;
 	char *target, *originator;
@@ -971,11 +971,13 @@ server_request_direct_tcpip(void)
 	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 &&
 	    !no_port_forwarding_flag) {
 		c = channel_connect_to(target, target_port,
-		    "direct-tcpip", "direct-tcpip");
+		    "direct-tcpip", "direct-tcpip", reason, errmsg);
 	} else {
 		logit("refused local port forward: "
 		    "originator %s port %d, target %s port %d",
 		    originator, originator_port, target, target_port);
+		if (reason != NULL)
+			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
 	}
 
 	free(originator);
@@ -1066,7 +1068,8 @@ server_input_channel_open(int type, u_in
 {
 	Channel *c = NULL;
 	char *ctype;
-	int rchan;
+	const char *errmsg = NULL;
+	int rchan, reason = SSH2_OPEN_CONNECT_FAILED;
 	u_int rmaxpack, rwindow, len;
 
 	ctype = packet_get_string(&len);
@@ -1080,7 +1083,7 @@ server_input_channel_open(int type, u_in
 	if (strcmp(ctype, "session") == 0) {
 		c = server_request_session();
 	} else if (strcmp(ctype, "direct-tcpip") == 0) {
-		c = server_request_direct_tcpip();
+		c = server_request_direct_tcpip(&reason, &errmsg);
 	} else if (strcmp(ctype, "tun@openssh.com") == 0) {
 		c = server_request_tun();
 	}
@@ -1101,9 +1104,9 @@ server_input_channel_open(int type, u_in
 		debug("server_input_channel_open: failure %s", ctype);
 		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
 		packet_put_int(rchan);
-		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
+		packet_put_int(reason);
 		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
-			packet_put_cstring("open failed");
+			packet_put_cstring(errmsg ? errmsg : "open failed");
 			packet_put_cstring("");
 		}
 		packet_send();
