[PATCH] permitremoteopen - to limit remote port forwarding per user

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



Hi,

Here is a patch to limit reverse port forwarding(-R) per user/key on the server.

For example add:
permitremoteopen="8023" ssh-dss AAAAB3NzaC1kc3MAAACBAOUE.. 

in user's ~/.ssh/authorized_keys server will limit -R to port 8023 only.

an example of violation.
ssh -v -R 8022:127.0.0.1:22 -i.ssh/id_dsa foo@10.0.0.1 
 
debug1: Remote: Server denied remote port forward request.
debug1: remote forward failure for: listen 8022, connect 127.0.0.1:22
Warning: remote port forwarding failed for listen port 8022 

and 
ssh -v -R 8023:127.0.0.1:22 -i.ssh/id_dsa foo@10.0.0.1  
will forward the port.

The patch should work on 6.6p1, 6.5p1, 6.4p1 and 6.6

regards,
-antony
>From 4324c285613f4eb98af9b8e01a3e9d5e26068031 Mon Sep 17 00:00:00 2001
From: Antony Antony <antony@xxxxxxxxxxx>
Date: Thu, 19 Jun 2014 20:01:22 +0200
Subject: [PATCH] permitremoteopen 6.6p1

---
 auth-options.c |  45 ++++++++++++++++++++++++
 channels.c     | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 channels.h     |   7 ++++
 servconf.c     |  34 +++++++++++++++++-
 servconf.h     |   1 +
 serverloop.c   |  15 +++++---
 session.c      |   8 +++--
 sshd.8         |  11 ++++++
 sshd_config.5  |  12 +++++++
 9 files changed, 233 insertions(+), 7 deletions(-)

diff --git a/auth-options.c b/auth-options.c
index fa209ea..31bb8e8 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -81,6 +81,7 @@ auth_clear_options(void)
 	}
 	forced_tun_device = -1;
 	channel_clear_permitted_opens();
+	channel_clear_permitted_remote_opens();
 }
 
 /*
@@ -350,6 +351,50 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
 			free(patterns);
 			goto next_option;
 		}
+		cp = "permitremoteopen=\"";
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+			char *p;
+			int port;
+			char *patterns = xmalloc(strlen(opts) + 1);
+
+			opts += strlen(cp);
+			i = 0;
+			while (*opts) {
+				if (*opts == '"')
+					break;
+				if (*opts == '\\' && opts[1] == '"') {
+					opts += 2;
+					patterns[i++] = '"';
+					continue;
+				}
+				patterns[i++] = *opts++;
+			}
+			if (!*opts) {
+				debug("%.100s, line %lu: missing end quote",
+				    file, linenum);
+				auth_debug_add("%.100s, line %lu: missing "
+				    "end quote", file, linenum);
+				free(patterns);
+				goto bad_option;
+			}
+			patterns[i] = '\0';
+			opts++;
+			p = patterns;
+			if (p == NULL || (port = permitopen_port(p)) < 0) {
+				debug("%.100s, line %lu: Bad permitremoteopen "
+				     "port <%.100s>", file, linenum, p ? p :
+				      "");
+				auth_debug_add("%.100s, line %lu: "
+						"Bad permitremoteopen port",
+						file, linenum);
+				free(patterns);
+				goto bad_option;
+			}
+			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
+				channel_add_permitted_remote_opens(port);
+			free(patterns);
+			goto next_option;
+		}
 		cp = "tunnel=\"";
 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			char *tun = NULL;
diff --git a/channels.c b/channels.c
index 9efe89c..08e4831 100644
--- a/channels.c
+++ b/channels.c
@@ -115,15 +115,19 @@ typedef struct {
 
 /* List of all permitted host/port pairs to connect by the user. */
 static ForwardPermission *permitted_opens = NULL;
+static ForwardPermission *permitted_remote_opens = NULL;
 
 /* List of all permitted host/port pairs to connect by the admin. */
 static ForwardPermission *permitted_adm_opens = NULL;
+static ForwardPermission *permitted_adm_remote_opens = NULL;
 
 /* Number of permitted host/port pairs in the array permitted by the user. */
 static int num_permitted_opens = 0;
+static int num_permitted_remote_opens = 0;
 
 /* Number of permitted host/port pair in the array permitted by the admin. */
 static int num_adm_permitted_opens = 0;
+static int num_adm_permitted_remote_opens = 0;
 
 /* special-case port number meaning allow any port */
 #define FWD_PERMIT_ANY_PORT	0
@@ -134,6 +138,7 @@ static int num_adm_permitted_opens = 0;
  * anything after logging in anyway.
  */
 static int all_opens_permitted = 0;
+static int all_remote_opens_permitted = 0;
 
 
 /* -- X11 forwarding */
@@ -3124,6 +3129,13 @@ channel_permit_all_opens(void)
 	if (num_permitted_opens == 0)
 		all_opens_permitted = 1;
 }
+void
+channel_permit_all_remote_opens(void)
+{
+	if (num_permitted_remote_opens == 0)
+		all_remote_opens_permitted = 1;
+}
+
 
 void
 channel_add_permitted_opens(char *host, int port)
@@ -3139,6 +3151,19 @@ channel_add_permitted_opens(char *host, int port)
 	all_opens_permitted = 0;
 }
 
+void
+channel_add_permitted_remote_opens(int port)
+{
+	debug("allow remote port forwarding %d", port);
+
+	permitted_remote_opens = xrealloc(permitted_remote_opens,
+	    num_permitted_remote_opens + 1, sizeof(*permitted_remote_opens));
+	permitted_remote_opens[num_permitted_opens].listen_port = port;
+	num_permitted_remote_opens++;
+
+	all_remote_opens_permitted = 0;
+}
+
 /*
  * Update the listen port for a dynamic remote forward, after
  * the actual 'newport' has been allocated. If 'newport' < 0 is
@@ -3181,6 +3206,18 @@ channel_add_adm_permitted_opens(char *host, int port)
 	return ++num_adm_permitted_opens;
 }
 
+int
+channel_add_adm_permitted_remote_opens(int port)
+{
+	debug("config allows remote port forwarding,  port %d", port);
+
+	permitted_adm_remote_opens = xrealloc(permitted_adm_remote_opens,
+	    num_adm_permitted_remote_opens + 1, sizeof(*permitted_adm_remote_opens));
+	permitted_adm_remote_opens[num_adm_permitted_remote_opens].listen_port = port;
+	return ++num_adm_permitted_remote_opens;
+}
+
+
 void
 channel_disable_adm_local_opens(void)
 {
@@ -3191,6 +3228,15 @@ channel_disable_adm_local_opens(void)
 }
 
 void
+channel_disable_adm_remote_opens(void)
+{
+	channel_clear_adm_permitted_remote_opens();
+	permitted_adm_remote_opens = xmalloc(sizeof(*permitted_adm_remote_opens));
+	permitted_adm_remote_opens[num_adm_permitted_remote_opens].host_to_connect = NULL;
+	num_adm_permitted_remote_opens = 1;
+}
+
+void
 channel_clear_permitted_opens(void)
 {
 	int i;
@@ -3203,6 +3249,16 @@ channel_clear_permitted_opens(void)
 }
 
 void
+channel_clear_permitted_remote_opens(void)
+{
+
+	free(permitted_remote_opens);
+	permitted_remote_opens = NULL;
+	num_permitted_remote_opens = 0;
+}
+
+
+void
 channel_clear_adm_permitted_opens(void)
 {
 	int i;
@@ -3215,6 +3271,15 @@ channel_clear_adm_permitted_opens(void)
 }
 
 void
+channel_clear_adm_permitted_remote_opens(void)
+{
+	free(permitted_adm_remote_opens);
+	permitted_adm_remote_opens = NULL;
+	num_adm_permitted_remote_opens = 0;
+}
+
+
+void
 channel_print_adm_permitted_opens(void)
 {
 	int i;
@@ -3399,6 +3464,48 @@ channel_connect_to(const char *host, u_short port, char *ctype, char *rname)
 	return connect_to(host, port, ctype, rname);
 }
 
+/* Check if remote port is permitted and connect. */
+int
+channel_connect_remote_to(u_short port)
+{
+	int i, permit, permit_adm = 1;
+	int allowed_port = 0;
+
+	permit = all_remote_opens_permitted;
+	if (!permit) {
+		for (i = 0; i < num_permitted_remote_opens; i++) {
+			allowed_port = permitted_remote_opens[i].listen_port;
+			debug("i=%d check remote permitted vs requested "
+					"%u vs %u", i, allowed_port, port);
+			if ( port_match(allowed_port, port)) {
+				debug2("i=%d found match remote permitted vs "
+						"requested %u==%u", i, allowed_port, port);
+				permit = 1;
+				break;
+			}
+		}
+	}
+	if (num_adm_permitted_remote_opens > 0) {
+		permit_adm = 0;
+		for (i = 0; i < num_adm_permitted_remote_opens; i++)
+			if (port_match(allowed_port, port) ) {
+				/*  && strcmp(permitted_adm_remote_opens[i].host_to_connect, host) == 0) */
+				debug2("i=%d found match admin remote permitted vs "
+						"requested %u==%u", i, allowed_port, port);
+				permit_adm = 1;
+			}
+	}
+
+	if (!permit || !permit_adm) {
+		logit("Received request to forward remote port %d, "
+		      "but the request was denied. return %d", port, permit);
+		return 0;
+	}
+	return ( permit | permit_adm);
+}
+
+
+
 void
 channel_send_window_changes(void)
 {
diff --git a/channels.h b/channels.h
index 4fab9d7..5ba4ec3 100644
--- a/channels.h
+++ b/channels.h
@@ -256,14 +256,21 @@ int	 channel_find_open(void);
 /* tcp forwarding */
 void	 channel_set_af(int af);
 void     channel_permit_all_opens(void);
+void     channel_permit_all_remote_opens(void);
 void	 channel_add_permitted_opens(char *, int);
+void	 channel_add_permitted_remote_opens(int);
 int	 channel_add_adm_permitted_opens(char *, int);
+int	 channel_add_adm_permitted_remote_opens(int);
 void	 channel_disable_adm_local_opens(void);
+void	 channel_disable_adm_remote_opens(void);
 void	 channel_update_permitted_opens(int, int);
 void	 channel_clear_permitted_opens(void);
 void	 channel_clear_adm_permitted_opens(void);
+void	 channel_clear_permitted_remote_opens(void);
+void	 channel_clear_adm_permitted_remote_opens(void);
 void 	 channel_print_adm_permitted_opens(void);
 int      channel_input_port_forward_request(int, int);
+int     channel_connect_remote_to(u_short);
 Channel	*channel_connect_to(const char *, u_short, char *, char *);
 Channel	*channel_connect_stdio_fwd(const char*, u_short, int, int);
 Channel	*channel_connect_by_listen_address(u_short, char *, char *);
diff --git a/servconf.c b/servconf.c
index 7ba65d5..cfefdc0 100644
--- a/servconf.c
+++ b/servconf.c
@@ -341,7 +341,7 @@ typedef enum {
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
 	sClientAliveCountMax, sAuthorizedKeysFile,
 	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
-	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
+	sMatch, sPermitOpen, sPermitRemoteOpen, sForceCommand, sChrootDirectory,
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
 	sHostCertificate,
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
@@ -462,6 +462,7 @@ static struct {
 	{ "permittty", sPermitTTY, SSHCFG_ALL },
 	{ "match", sMatch, SSHCFG_ALL },
 	{ "permitopen", sPermitOpen, SSHCFG_ALL },
+	{ "permitremoteopen", sPermitRemoteOpen, SSHCFG_ALL },
 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
 	{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
@@ -1521,6 +1522,37 @@ process_server_config_line(ServerOptions *options, char *line,
 				    channel_add_adm_permitted_opens(p, port);
 		}
 		break;
+	case sPermitRemoteOpen:
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: missing PermitRemoteOpen "
+				" specification", filename, linenum);
+		n = options->num_permitted_remote_opens; /* modified later */
+		if (strcmp(arg, "any") == 0) {
+			if (*activep && n == -1) {
+				channel_clear_adm_permitted_remote_opens();
+				options->num_permitted_remote_opens = 0;
+			}
+			break;
+		}
+		if (strcmp(arg, "none") == 0) {
+			if (*activep && n == -1) {
+				options->num_permitted_remote_opens = 1;
+				channel_disable_adm_remote_opens();
+			}
+			break;
+		}
+		if (*activep && n == -1)
+			channel_clear_adm_permitted_remote_opens();
+		for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) {
+			if (arg == NULL || ((port = permitopen_port(arg)) < 0))
+				fatal("%s line %d: bad port number in "
+				      "PermitRemoteOpen", filename, linenum);
+			if (*activep && n == -1)
+				options->num_permitted_remote_opens =
+					channel_add_adm_permitted_remote_opens(port);
+		}
+		break;
 
 	case sForceCommand:
 		if (cp == NULL)
diff --git a/servconf.h b/servconf.h
index 752d1c5..9f1c2f9 100644
--- a/servconf.h
+++ b/servconf.h
@@ -168,6 +168,7 @@ typedef struct {
 	int	permit_tun;
 
 	int	num_permitted_opens;
+	int	num_permitted_remote_opens;
 
 	char   *chroot_directory;
 	char   *revoked_keys_file;
diff --git a/serverloop.c b/serverloop.c
index 2f8e3a0..1e86bb9 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1148,10 +1148,17 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
 			success = 0;
 			packet_send_debug("Server has disabled port forwarding.");
 		} else {
-			/* Start listening on the port */
-			success = channel_setup_remote_fwd_listener(
-			    listen_address, listen_port,
-			    &allocated_listen_port, options.gateway_ports);
+			debug2("check permitted remote opens");
+			if (!channel_connect_remote_to(listen_port)) {
+				success = 0;
+				packet_send_debug("Server denied remote port forward request.");
+			}
+			else {
+				/* Start listening on the port */
+				success = channel_setup_remote_fwd_listener(
+						listen_address, listen_port,
+						&allocated_listen_port, options.gateway_ports);
+			}
 		}
 		free(listen_address);
 	} else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
diff --git a/session.c b/session.c
index 2bcf818..78c6293 100644
--- a/session.c
+++ b/session.c
@@ -274,10 +274,14 @@ do_authenticated(Authctxt *authctxt)
 
 	/* setup the channel layer */
 	if (no_port_forwarding_flag ||
-	    (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
+	    (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) {
 		channel_disable_adm_local_opens();
-	else
+		channel_disable_adm_remote_opens();
+	}
+	else {
 		channel_permit_all_opens();
+		channel_permit_all_remote_opens();
+	}
 
 	auth_debug_send();
 
diff --git a/sshd.8 b/sshd.8
index e6a900b..08d5e7a 100644
--- a/sshd.8
+++ b/sshd.8
@@ -617,6 +617,17 @@ they must be literal domains or addresses.
 A port specification of
 .Cm *
 matches any port.
+.It Cm permitremoteopen="port"
+Limit remote
+.Li ``ssh -R''
+port forwarding such that it may only forward the specified port to the client.
+Multiple
+.Cm permitremoteopen
+options may be applied separated by commas.
+Specified port must be a single port.
+A port specification of
+.Cm *
+matches any port.
 .It Cm principals="principals"
 On a
 .Cm cert-authority
diff --git a/sshd_config.5 b/sshd_config.5
index ce71efe..d192f2d 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -922,6 +922,18 @@ An argument of
 .Dq none
 can be used to prohibit all forwarding requests.
 By default all port forwarding requests are permitted.
+.It Cm PermitRemoteOpen
+Specifies the TCP ports which may be opened by clients with the
+RemoteForward option.
+Multiple forwards may be specified by separating them with whiespace.
+An argument of
+.Dq any
+can be used to remove all restrictions and permit any remote
+forwarding requests.
+An argument of
+.Dq none
+can be used to prohibit all forwarding requests.
+By default all port forwarding requests are permitted.
 .It Cm PermitRootLogin
 Specifies whether root can log in using
 .Xr ssh 1 .
-- 
1.9.1

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux