[openssh-commits] [openssh] 01/04: upstream: Add a PermitListen directive to control which server-side

git+noreply at mindrot.org git+noreply at mindrot.org
Thu Jun 7 04:27:30 AEST 2018


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 115063a6647007286cc8ca70abfd2a7585f26ccc
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Wed Jun 6 18:22:41 2018 +0000

    upstream: Add a PermitListen directive to control which server-side
    
    addresses may be listened on when the client requests remote forwarding (ssh
    -R).
    
    This is the converse of the existing PermitOpen directive and this
    includes some refactoring to share much of its implementation.
    
    feedback and ok markus@
    
    OpenBSD-Commit-ID: 15a931238c61a3f2ac74ea18a98c933e358e277f
---
 channels.c | 475 +++++++++++++++++++++++++++++++++++++++----------------------
 channels.h |  26 ++--
 mux.c      |   6 +-
 servconf.c | 138 +++++++++++-------
 servconf.h |  16 +--
 session.c  |  27 ++--
 ssh.c      |   6 +-
 7 files changed, 438 insertions(+), 256 deletions(-)

diff --git a/channels.c b/channels.c
index 65d9dbd5..1b40d7da 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.380 2018/04/10 00:10:49 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.381 2018/06/06 18:22:41 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -82,6 +82,7 @@
 #include "key.h"
 #include "authfd.h"
 #include "pathnames.h"
+#include "match.h"
 
 /* -- agent forwarding */
 #define	NUM_SOCKS	10
@@ -97,6 +98,10 @@
 /* Maximum number of fake X11 displays to try. */
 #define MAX_DISPLAYS  1000
 
+/* Per-channel callback for pre/post select() actions */
+typedef void chan_fn(struct ssh *, Channel *c,
+    fd_set *readset, fd_set *writeset);
+
 /*
  * Data structure for storing which hosts are permitted for forward requests.
  * The local sides of any remote forwards are stored in this array to prevent
@@ -106,17 +111,40 @@
 /* XXX: streamlocal wants a path instead of host:port */
 /*      Overload host_to_connect; we could just make this match Forward */
 /*	XXX - can we use listen_host instead of listen_path? */
-typedef struct {
+struct permission {
 	char *host_to_connect;		/* Connect to 'host'. */
 	int port_to_connect;		/* Connect to 'port'. */
 	char *listen_host;		/* Remote side should listen address. */
 	char *listen_path;		/* Remote side should listen path. */
 	int listen_port;		/* Remote side should listen port. */
 	Channel *downstream;		/* Downstream mux*/
-} ForwardPermission;
+};
 
-typedef void chan_fn(struct ssh *, Channel *c,
-    fd_set *readset, fd_set *writeset);
+/*
+ * Stores the forwarding permission state for a single direction (local or
+ * remote).
+ */
+struct permission_set {
+	/*
+	 * List of all local permitted host/port pairs to allow for the
+	 * user.
+	 */
+	u_int num_permitted_user;
+	struct permission *permitted_user;
+
+	/*
+	 * List of all permitted host/port pairs to allow for the admin.
+	 */
+	u_int num_permitted_admin;
+	struct permission *permitted_admin;
+
+	/*
+	 * If this is true, all opens/listens are permitted.  This is the
+	 * case on the server on which we have to trust the client anyway,
+	 * and the user could do anything after logging in.
+	 */
+	int all_permitted;
+};
 
 /* Master structure for channels state */
 struct ssh_channels {
@@ -149,31 +177,8 @@ struct ssh_channels {
 	chan_fn **channel_post;
 
 	/* -- tcp forwarding */
-
-	/* List of all permitted host/port pairs to connect by the user. */
-	ForwardPermission *permitted_opens;
-
-	/* List of all permitted host/port pairs to connect by the admin. */
-	ForwardPermission *permitted_adm_opens;
-
-	/*
-	 * Number of permitted host/port pairs in the array permitted by
-	 * the user.
-	 */
-	u_int num_permitted_opens;
-
-	/*
-	 * Number of permitted host/port pair in the array permitted by
-	 * the admin.
-	 */
-	u_int num_adm_permitted_opens;
-
-	/*
-	 * If this is true, all opens are permitted.  This is the case on
-	 * the server on which we have to trust the client anyway, and the
-	 * user could do anything after logging in anyway.
-	 */
-	int all_opens_permitted;
+	struct permission_set local_perms;
+	struct permission_set remote_perms;
 
 	/* -- X11 forwarding */
 
@@ -448,50 +453,95 @@ channel_close_fds(struct ssh *ssh, Channel *c)
 }
 
 static void
-fwd_perm_clear(ForwardPermission *fp)
+fwd_perm_clear(struct permission *perm)
 {
-	free(fp->host_to_connect);
-	free(fp->listen_host);
-	free(fp->listen_path);
-	bzero(fp, sizeof(*fp));
+	free(perm->host_to_connect);
+	free(perm->listen_host);
+	free(perm->listen_path);
+	bzero(perm, sizeof(*perm));
 }
 
-enum { FWDPERM_USER, FWDPERM_ADMIN };
+/* Returns an printable name for the specified forwarding permission list */
+static const char *
+fwd_ident(int who, int where)
+{
+	if (who == FORWARD_ADM) {
+		if (where == FORWARD_LOCAL)
+			return "admin local";
+		else if (where == FORWARD_REMOTE)
+			return "admin remote";
+	} else if (who == FORWARD_USER) {
+		if (where == FORWARD_LOCAL)
+			return "user local";
+		else if (where == FORWARD_REMOTE)
+			return "user remote";
+	}
+	fatal("Unknown forward permission list %d/%d", who, where);
+}
+
+/* Returns the forwarding permission list for the specified direction */
+static struct permission_set *
+permission_set_get(struct ssh *ssh, int where)
+{
+	struct ssh_channels *sc = ssh->chanctxt;
+
+	switch (where) {
+	case FORWARD_LOCAL:
+		return &sc->local_perms;
+		break;
+	case FORWARD_REMOTE:
+		return &sc->remote_perms;
+		break;
+	default:
+		fatal("%s: invalid forwarding direction %d", __func__, where);
+	}
+}
+
+/* Reutrns pointers to the specified forwarding list and its element count */
+static void
+permission_set_get_array(struct ssh *ssh, int who, int where,
+    struct permission ***permpp, u_int **npermpp)
+{
+	struct permission_set *pset = permission_set_get(ssh, where);
+
+	switch (who) {
+	case FORWARD_USER:
+		*permpp = &pset->permitted_user;
+		*npermpp = &pset->num_permitted_user;
+		break;
+	case FORWARD_ADM:
+		*permpp = &pset->permitted_admin;
+		*npermpp = &pset->num_permitted_admin;
+		break;
+	default:
+		fatal("%s: invalid forwarding client %d", __func__, who);
+	}
+}
 
+/* Adds an entry to the spcified forwarding list */
 static int
-fwd_perm_list_add(struct ssh *ssh, int which,
+permission_set_add(struct ssh *ssh, int who, int where,
     const char *host_to_connect, int port_to_connect,
     const char *listen_host, const char *listen_path, int listen_port,
     Channel *downstream)
 {
-	ForwardPermission **fpl;
-	u_int n, *nfpl;
+	struct permission **permp;
+	u_int n, *npermp;
 
-	switch (which) {
-	case FWDPERM_USER:
-		fpl = &ssh->chanctxt->permitted_opens;
-		nfpl = &ssh->chanctxt->num_permitted_opens;
-		break;
-	case FWDPERM_ADMIN:
-		fpl = &ssh->chanctxt->permitted_adm_opens;
-		nfpl = &ssh->chanctxt->num_adm_permitted_opens;
-		break;
-	default:
-		fatal("%s: invalid list %d", __func__, which);
-	}
+	permission_set_get_array(ssh, who, where, &permp, &npermp);
 
-	if (*nfpl >= INT_MAX)
-		fatal("%s: overflow", __func__);
+	if (*npermp >= INT_MAX)
+		fatal("%s: %s overflow", __func__, fwd_ident(who, where));
 
-	*fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl));
-	n = (*nfpl)++;
+	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
+	n = (*npermp)++;
 #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
-	(*fpl)[n].host_to_connect = MAYBE_DUP(host_to_connect);
-	(*fpl)[n].port_to_connect = port_to_connect;
-	(*fpl)[n].listen_host = MAYBE_DUP(listen_host);
-	(*fpl)[n].listen_path = MAYBE_DUP(listen_path);
-	(*fpl)[n].listen_port = listen_port;
-	(*fpl)[n].downstream = downstream;
+	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
+	(*permp)[n].port_to_connect = port_to_connect;
+	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
+	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
+	(*permp)[n].listen_port = listen_port;
+	(*permp)[n].downstream = downstream;
 #undef MAYBE_DUP
 	return (int)n;
 }
@@ -500,30 +550,31 @@ static void
 mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
-	ForwardPermission *fp;
+	struct permission_set *pset = &sc->local_perms;
+	struct permission *perm;
 	int r;
 	u_int i;
 
-	for (i = 0; i < sc->num_permitted_opens; i++) {
-		fp = &sc->permitted_opens[i];
-		if (fp->downstream != c)
+	for (i = 0; i < pset->num_permitted_user; i++) {
+		perm = &pset->permitted_user[i];
+		if (perm->downstream != c)
 			continue;
 
 		/* cancel on the server, since mux client is gone */
 		debug("channel %d: cleanup remote forward for %s:%u",
-		    c->self, fp->listen_host, fp->listen_port);
+		    c->self, perm->listen_host, perm->listen_port);
 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
 		    (r = sshpkt_put_cstring(ssh,
 		    "cancel-tcpip-forward")) != 0 ||
 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
 		    (r = sshpkt_put_cstring(ssh,
-		    channel_rfwd_bind_host(fp->listen_host))) != 0 ||
-		    (r = sshpkt_put_u32(ssh, fp->listen_port)) != 0 ||
+		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
+		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
 		    (r = sshpkt_send(ssh)) != 0) {
 			fatal("%s: channel %i: %s", __func__,
 			    c->self, ssh_err(r));
 		}
-		fwd_perm_clear(fp); /* unregister */
+		fwd_perm_clear(perm); /* unregister */
 	}
 }
 
@@ -2729,7 +2780,7 @@ channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
 			goto out;
 		}
 		/* Record that connection to this host/port is permitted. */
-		fwd_perm_list_add(ssh, FWDPERM_USER, "<mux>", -1,
+		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1,
 		    listen_host, NULL, (int)listen_port, downstream);
 		listen_host = NULL;
 		break;
@@ -3637,11 +3688,78 @@ channel_setup_local_fwd_listener(struct ssh *ssh,
 	}
 }
 
+/* Matches a remote forwarding permission against a requested forwarding */
+static int
+remote_open_match(struct permission *allowed_open, struct Forward *fwd)
+{
+	int ret;
+	char *lhost;
+
+	/* XXX add ACLs for streamlocal */
+	if (fwd->listen_path != NULL)
+		return 1;
+
+	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
+		return 0;
+
+	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
+	    allowed_open->listen_port != fwd->listen_port)
+		return 0;
+
+	/* Match hostnames case-insensitively */
+	lhost = xstrdup(fwd->listen_host);
+	lowercase(lhost);
+	ret = match_pattern(lhost, allowed_open->listen_host);
+	free(lhost);
+
+	return ret;
+}
+
+/* Checks whether a requested remote forwarding is permitted */
+static int
+check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
+{
+	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->remote_perms;
+	u_int i, permit, permit_adm = 1;
+	struct permission *perm;
+
+	/* XXX apply GatewayPorts override before checking? */
+
+	permit = pset->all_permitted;
+	if (!permit) {
+		for (i = 0; i < pset->num_permitted_user; i++) {
+			perm = &pset->permitted_user[i];
+			if (remote_open_match(perm, fwd)) {
+				permit = 1;
+				break;
+			}
+		}
+	}
+
+	if (pset->num_permitted_admin > 0) {
+		permit_adm = 0;
+		for (i = 0; i < pset->num_permitted_admin; i++) {
+			perm = &pset->permitted_admin[i];
+			if (remote_open_match(perm, fwd)) {
+				permit_adm = 1;
+				break;
+			}
+		}
+	}
+
+	return permit && permit_adm;
+}
+
 /* protocol v2 remote port fwd, used by sshd */
 int
 channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
 {
+	if (!check_rfwd_permission(ssh, fwd)) {
+		packet_send_debug("port forwarding refused");
+		return 0;
+	}
 	if (fwd->listen_path != NULL) {
 		return channel_setup_fwd_listener_streamlocal(ssh,
 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
@@ -3671,7 +3789,7 @@ channel_rfwd_bind_host(const char *listen_host)
  * Initiate forwarding of connections to port "port" on remote host through
  * the secure channel to host:port from local side.
  * Returns handle (index) for updating the dynamic listen port with
- * channel_update_permitted_opens().
+ * channel_update_permission().
  */
 int
 channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
@@ -3724,7 +3842,7 @@ channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
 				listen_host = xstrdup(fwd->listen_host);
 			listen_port = fwd->listen_port;
 		}
-		idx = fwd_perm_list_add(ssh, FWDPERM_USER,
+		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
 		    host_to_connect, port_to_connect,
 		    listen_host, listen_path, listen_port, NULL);
 	}
@@ -3732,7 +3850,7 @@ channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
 }
 
 static int
-open_match(ForwardPermission *allowed_open, const char *requestedhost,
+open_match(struct permission *allowed_open, const char *requestedhost,
     int requestedport)
 {
 	if (allowed_open->host_to_connect == NULL)
@@ -3753,7 +3871,7 @@ open_match(ForwardPermission *allowed_open, const char *requestedhost,
  * and what we've sent to the remote server (channel_rfwd_bind_host)
  */
 static int
-open_listen_match_tcpip(ForwardPermission *allowed_open,
+open_listen_match_tcpip(struct permission *allowed_open,
     const char *requestedhost, u_short requestedport, int translate)
 {
 	const char *allowed_host;
@@ -3775,7 +3893,7 @@ open_listen_match_tcpip(ForwardPermission *allowed_open,
 }
 
 static int
-open_listen_match_streamlocal(ForwardPermission *allowed_open,
+open_listen_match_streamlocal(struct permission *allowed_open,
     const char *requestedpath)
 {
 	if (allowed_open->host_to_connect == NULL)
@@ -3797,17 +3915,18 @@ channel_request_rforward_cancel_tcpip(struct ssh *ssh,
     const char *host, u_short port)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	int r;
 	u_int i;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	for (i = 0; i < sc->num_permitted_opens; i++) {
-		fp = &sc->permitted_opens[i];
-		if (open_listen_match_tcpip(fp, host, port, 0))
+	for (i = 0; i < pset->num_permitted_user; i++) {
+		perm = &pset->permitted_user[i];
+		if (open_listen_match_tcpip(perm, host, port, 0))
 			break;
-		fp = NULL;
+		perm = NULL;
 	}
-	if (fp == NULL) {
+	if (perm == NULL) {
 		debug("%s: requested forward not found", __func__);
 		return -1;
 	}
@@ -3819,7 +3938,7 @@ channel_request_rforward_cancel_tcpip(struct ssh *ssh,
 	    (r = sshpkt_send(ssh)) != 0)
 		fatal("%s: send cancel: %s", __func__, ssh_err(r));
 
-	fwd_perm_clear(fp); /* unregister */
+	fwd_perm_clear(perm); /* unregister */
 
 	return 0;
 }
@@ -3832,17 +3951,18 @@ static int
 channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	int r;
 	u_int i;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	for (i = 0; i < sc->num_permitted_opens; i++) {
-		fp = &sc->permitted_opens[i];
-		if (open_listen_match_streamlocal(fp, path))
+	for (i = 0; i < pset->num_permitted_user; i++) {
+		perm = &pset->permitted_user[i];
+		if (open_listen_match_streamlocal(perm, path))
 			break;
-		fp = NULL;
+		perm = NULL;
 	}
-	if (fp == NULL) {
+	if (perm == NULL) {
 		debug("%s: requested forward not found", __func__);
 		return -1;
 	}
@@ -3854,7 +3974,7 @@ channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
 	    (r = sshpkt_send(ssh)) != 0)
 		fatal("%s: send cancel: %s", __func__, ssh_err(r));
 
-	fwd_perm_clear(fp); /* unregister */
+	fwd_perm_clear(perm); /* unregister */
 
 	return 0;
 }
@@ -3876,25 +3996,64 @@ channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
 }
 
 /*
- * Permits opening to any host/port if permitted_opens[] is empty.  This is
+ * Permits opening to any host/port if permitted_user[] is empty.  This is
  * usually called by the server, because the user could connect to any port
  * anyway, and the server has no way to know but to trust the client anyway.
  */
 void
-channel_permit_all_opens(struct ssh *ssh)
+channel_permit_all(struct ssh *ssh, int where)
 {
-	if (ssh->chanctxt->num_permitted_opens == 0)
-		ssh->chanctxt->all_opens_permitted = 1;
+	struct permission_set *pset = permission_set_get(ssh, where);
+
+	if (pset->num_permitted_user == 0)
+		pset->all_permitted = 1;
+}
+
+/*
+ * Permit the specified host/port for forwarding.
+ */
+void
+channel_add_permission(struct ssh *ssh, int who, int where,
+    char *host, int port)
+{
+	int local = where == FORWARD_LOCAL;
+	struct permission_set *pset = permission_set_get(ssh, where);
+
+	debug("allow %s forwarding to host %s port %d",
+	    fwd_ident(who, where), host, port);
+	/*
+	 * Remote forwards set listen_host/port, local forwards set
+	 * host/port_to_connect.
+	 */
+	permission_set_add(ssh, who, where,
+	    local ? host : 0, local ? port : 0,
+	    local ? NULL : host, NULL, local ? 0 : port, NULL);
+	pset->all_permitted = 0;
+}
+
+/*
+ * Administratively disable forwarding.
+ */
+void
+channel_disable_admin(struct ssh *ssh, int where)
+{
+	channel_clear_permission(ssh, FORWARD_ADM, where);
+	permission_set_add(ssh, FORWARD_ADM, where,
+	    NULL, 0, NULL, NULL, 0, NULL);
 }
 
+/*
+ * Clear a list of permitted opens.
+ */
 void
-channel_add_permitted_opens(struct ssh *ssh, char *host, int port)
+channel_clear_permission(struct ssh *ssh, int who, int where)
 {
-	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission **permp;
+	u_int *npermp;
 
-	debug("allow port forwarding to host %s port %d", host, port);
-	fwd_perm_list_add(ssh, FWDPERM_USER, host, port, NULL, NULL, 0, NULL);
-	sc->all_opens_permitted = 0;
+	permission_set_get_array(ssh, who, where, &permp, &npermp);
+	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
+	*npermp = 0;
 }
 
 /*
@@ -3903,63 +4062,28 @@ channel_add_permitted_opens(struct ssh *ssh, char *host, int port)
  * passed then they entry will be invalidated.
  */
 void
-channel_update_permitted_opens(struct ssh *ssh, int idx, int newport)
+channel_update_permission(struct ssh *ssh, int idx, int newport)
 {
-	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &ssh->chanctxt->local_perms;
 
-	if (idx < 0 || (u_int)idx >= sc->num_permitted_opens) {
-		debug("%s: index out of range: %d num_permitted_opens %d",
-		    __func__, idx, sc->num_permitted_opens);
+	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
+		debug("%s: index out of range: %d num_permitted_user %d",
+		    __func__, idx, pset->num_permitted_user);
 		return;
 	}
 	debug("%s allowed port %d for forwarding to host %s port %d",
 	    newport > 0 ? "Updating" : "Removing",
 	    newport,
-	    sc->permitted_opens[idx].host_to_connect,
-	    sc->permitted_opens[idx].port_to_connect);
+	    pset->permitted_user[idx].host_to_connect,
+	    pset->permitted_user[idx].port_to_connect);
 	if (newport <= 0)
-		fwd_perm_clear(&sc->permitted_opens[idx]);
+		fwd_perm_clear(&pset->permitted_user[idx]);
 	else {
-		sc->permitted_opens[idx].listen_port =
+		pset->permitted_user[idx].listen_port =
 		    (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
 	}
 }
 
-int
-channel_add_adm_permitted_opens(struct ssh *ssh, char *host, int port)
-{
-	debug("config allows port forwarding to host %s port %d", host, port);
-	return fwd_perm_list_add(ssh, FWDPERM_ADMIN, host, port,
-	    NULL, NULL, 0, NULL);
-}
-
-void
-channel_disable_adm_local_opens(struct ssh *ssh)
-{
-	channel_clear_adm_permitted_opens(ssh);
-	fwd_perm_list_add(ssh, FWDPERM_ADMIN, NULL, 0, NULL, NULL, 0, NULL);
-}
-
-void
-channel_clear_permitted_opens(struct ssh *ssh)
-{
-	struct ssh_channels *sc = ssh->chanctxt;
-
-	sc->permitted_opens = xrecallocarray(sc->permitted_opens,
-	    sc->num_permitted_opens, 0, sizeof(*sc->permitted_opens));
-	sc->num_permitted_opens = 0;
-}
-
-void
-channel_clear_adm_permitted_opens(struct ssh *ssh)
-{
-	struct ssh_channels *sc = ssh->chanctxt;
-
-	sc->permitted_adm_opens = xrecallocarray(sc->permitted_adm_opens,
-	    sc->num_adm_permitted_opens, 0, sizeof(*sc->permitted_adm_opens));
-	sc->num_adm_permitted_opens = 0;
-}
-
 /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
 int
 permitopen_port(const char *p)
@@ -4148,19 +4272,21 @@ channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
     u_short listen_port, char *ctype, char *rname)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	u_int i;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	for (i = 0; i < sc->num_permitted_opens; i++) {
-		fp = &sc->permitted_opens[i];
-		if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) {
-			if (fp->downstream)
-				return fp->downstream;
-			if (fp->port_to_connect == 0)
+	for (i = 0; i < pset->num_permitted_user; i++) {
+		perm = &pset->permitted_user[i];
+		if (open_listen_match_tcpip(perm,
+		    listen_host, listen_port, 1)) {
+			if (perm->downstream)
+				return perm->downstream;
+			if (perm->port_to_connect == 0)
 				return rdynamic_connect_prepare(ssh,
 				    ctype, rname);
 			return connect_to(ssh,
-			    fp->host_to_connect, fp->port_to_connect,
+			    perm->host_to_connect, perm->port_to_connect,
 			    ctype, rname);
 		}
 	}
@@ -4174,14 +4300,15 @@ channel_connect_by_listen_path(struct ssh *ssh, const char *path,
     char *ctype, char *rname)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	u_int i;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	for (i = 0; i < sc->num_permitted_opens; i++) {
-		fp = &sc->permitted_opens[i];
-		if (open_listen_match_streamlocal(fp, path)) {
+	for (i = 0; i < pset->num_permitted_user; i++) {
+		perm = &pset->permitted_user[i];
+		if (open_listen_match_streamlocal(perm, path)) {
 			return connect_to(ssh,
-			    fp->host_to_connect, fp->port_to_connect,
+			    perm->host_to_connect, perm->port_to_connect,
 			    ctype, rname);
 		}
 	}
@@ -4196,28 +4323,29 @@ channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
     char *ctype, char *rname, int *reason, const char **errmsg)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	struct channel_connect cctx;
 	Channel *c;
 	u_int i, permit, permit_adm = 1;
 	int sock;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	permit = sc->all_opens_permitted;
+	permit = pset->all_permitted;
 	if (!permit) {
-		for (i = 0; i < sc->num_permitted_opens; i++) {
-			fp = &sc->permitted_opens[i];
-			if (open_match(fp, host, port)) {
+		for (i = 0; i < pset->num_permitted_user; i++) {
+			perm = &pset->permitted_user[i];
+			if (open_match(perm, host, port)) {
 				permit = 1;
 				break;
 			}
 		}
 	}
 
-	if (sc->num_adm_permitted_opens > 0) {
+	if (pset->num_permitted_admin > 0) {
 		permit_adm = 0;
-		for (i = 0; i < sc->num_adm_permitted_opens; i++) {
-			fp = &sc->permitted_adm_opens[i];
-			if (open_match(fp, host, port)) {
+		for (i = 0; i < pset->num_permitted_admin; i++) {
+			perm = &pset->permitted_admin[i];
+			if (open_match(perm, host, port)) {
 				permit_adm = 1;
 				break;
 			}
@@ -4255,25 +4383,26 @@ channel_connect_to_path(struct ssh *ssh, const char *path,
     char *ctype, char *rname)
 {
 	struct ssh_channels *sc = ssh->chanctxt;
+	struct permission_set *pset = &sc->local_perms;
 	u_int i, permit, permit_adm = 1;
-	ForwardPermission *fp;
+	struct permission *perm;
 
-	permit = sc->all_opens_permitted;
+	permit = pset->all_permitted;
 	if (!permit) {
-		for (i = 0; i < sc->num_permitted_opens; i++) {
-			fp = &sc->permitted_opens[i];
-			if (open_match(fp, path, PORT_STREAMLOCAL)) {
+		for (i = 0; i < pset->num_permitted_user; i++) {
+			perm = &pset->permitted_user[i];
+			if (open_match(perm, path, PORT_STREAMLOCAL)) {
 				permit = 1;
 				break;
 			}
 		}
 	}
 
-	if (sc->num_adm_permitted_opens > 0) {
+	if (pset->num_permitted_admin > 0) {
 		permit_adm = 0;
-		for (i = 0; i < sc->num_adm_permitted_opens; i++) {
-			fp = &sc->permitted_adm_opens[i];
-			if (open_match(fp, path, PORT_STREAMLOCAL)) {
+		for (i = 0; i < pset->num_permitted_admin; i++) {
+			perm = &pset->permitted_admin[i];
+			if (open_match(perm, path, PORT_STREAMLOCAL)) {
 				permit_adm = 1;
 				break;
 			}
diff --git a/channels.h b/channels.h
index 126b0434..1aeafe94 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.130 2017/09/21 19:16:53 markus Exp $ */
+/* $OpenBSD: channels.h,v 1.131 2018/06/06 18:22:41 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -63,6 +63,15 @@
 
 #define CHANNEL_CANCEL_PORT_STATIC	-1
 
+/* TCP forwarding */
+#define FORWARD_DENY		0
+#define FORWARD_REMOTE		(1)
+#define FORWARD_LOCAL		(1<<1)
+#define FORWARD_ALLOW		(FORWARD_REMOTE|FORWARD_LOCAL)
+
+#define FORWARD_ADM		0x100
+#define FORWARD_USER		0x101
+
 struct ssh;
 struct Channel;
 typedef struct Channel Channel;
@@ -283,16 +292,11 @@ int	 channel_find_open(struct ssh *);
 struct Forward;
 struct ForwardOptions;
 void	 channel_set_af(struct ssh *, int af);
-void     channel_permit_all_opens(struct ssh *);
-void	 channel_add_permitted_opens(struct ssh *, char *, int);
-int	 channel_add_adm_permitted_opens(struct ssh *, char *, int);
-void	 channel_copy_adm_permitted_opens(struct ssh *,
-	    const struct fwd_perm_list *);
-void	 channel_disable_adm_local_opens(struct ssh *);
-void	 channel_update_permitted_opens(struct ssh *, int, int);
-void	 channel_clear_permitted_opens(struct ssh *);
-void	 channel_clear_adm_permitted_opens(struct ssh *);
-void 	 channel_print_adm_permitted_opens(struct ssh *);
+void     channel_permit_all(struct ssh *, int);
+void	 channel_add_permission(struct ssh *, int, int, char *, int);
+void	 channel_clear_permission(struct ssh *, int, int);
+void	 channel_disable_admin(struct ssh *, int);
+void	 channel_update_permission(struct ssh *, int, int);
 Channel	*channel_connect_to_port(struct ssh *, const char *, u_short,
 	    char *, char *, int *, const char **);
 Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *);
diff --git a/mux.c b/mux.c
index 5ae45441..c591cb15 100644
--- a/mux.c
+++ b/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.69 2017/09/20 05:19:00 dtucker Exp $ */
+/* $OpenBSD: mux.c,v 1.70 2018/06/06 18:22:41 djm Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm at openbsd.org>
  *
@@ -634,7 +634,7 @@ mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)
 			buffer_put_int(&out, MUX_S_REMOTE_PORT);
 			buffer_put_int(&out, fctx->rid);
 			buffer_put_int(&out, rfwd->allocated_port);
-			channel_update_permitted_opens(ssh, rfwd->handle,
+			channel_update_permission(ssh, rfwd->handle,
 			   rfwd->allocated_port);
 		} else {
 			buffer_put_int(&out, MUX_S_OK);
@@ -643,7 +643,7 @@ mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)
 		goto out;
 	} else {
 		if (rfwd->listen_port == 0)
-			channel_update_permitted_opens(ssh, rfwd->handle, -1);
+			channel_update_permission(ssh, rfwd->handle, -1);
 		if (rfwd->listen_path != NULL)
 			xasprintf(&failmsg, "remote port forwarding failed for "
 			    "listen path %s", rfwd->listen_path);
diff --git a/servconf.c b/servconf.c
index 5ca84515..b75faf3f 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,5 +1,5 @@
 
-/* $OpenBSD: servconf.c,v 1.328 2018/04/10 00:10:49 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.329 2018/06/06 18:22:41 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -160,6 +160,7 @@ initialize_server_options(ServerOptions *options)
 	options->num_accept_env = 0;
 	options->permit_tun = -1;
 	options->permitted_opens = NULL;
+	options->permitted_remote_opens = NULL;
 	options->adm_forced_command = NULL;
 	options->chroot_directory = NULL;
 	options->authorized_keys_command = NULL;
@@ -462,7 +463,7 @@ typedef enum {
 	sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
 	sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
 	sAcceptEnv, sPermitTunnel,
-	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
+	sMatch, sPermitOpen, sPermitRemoteOpen, sForceCommand, sChrootDirectory,
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
 	sHostCertificate,
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
@@ -597,6 +598,7 @@ static struct {
 	{ "permituserrc", sPermitUserRC, 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 },
@@ -632,6 +634,20 @@ static struct {
 	{ -1, NULL }
 };
 
+/* Returns an opcode name from its number */
+
+static const char *
+lookup_opcode_name(ServerOpCodes code)
+{
+	u_int i;
+
+	for (i = 0; keywords[i].name != NULL; i++)
+		if (keywords[i].opcode == code)
+			return(keywords[i].name);
+	return "UNKNOWN";
+}
+
+
 /*
  * Returns the number of the token pointed to by cp or sBadOption.
  */
@@ -814,43 +830,59 @@ process_queued_listen_addrs(ServerOptions *options)
 }
 
 /*
- * Inform channels layer of permitopen options from configuration.
+ * Inform channels layer of permitopen options for a single forwarding
+ * direction (local/remote).
  */
-void
-process_permitopen(struct ssh *ssh, ServerOptions *options)
+static void
+process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode,
+    char **opens, u_int num_opens)
 {
 	u_int i;
 	int port;
 	char *host, *arg, *oarg;
+	int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE;
+	const char *what = lookup_opcode_name(opcode);
 
-	channel_clear_adm_permitted_opens(ssh);
-	if (options->num_permitted_opens == 0)
+	channel_clear_permission(ssh, FORWARD_ADM, where);
+	if (num_opens == 0)
 		return; /* permit any */
 
 	/* handle keywords: "any" / "none" */
-	if (options->num_permitted_opens == 1 &&
-	    strcmp(options->permitted_opens[0], "any") == 0)
+	if (num_opens == 1 && strcmp(opens[0], "any") == 0)
 		return;
-	if (options->num_permitted_opens == 1 &&
-	    strcmp(options->permitted_opens[0], "none") == 0) {
-		channel_disable_adm_local_opens(ssh);
+	if (num_opens == 1 && strcmp(opens[0], "none") == 0) {
+		channel_disable_admin(ssh, where);
 		return;
 	}
 	/* Otherwise treat it as a list of permitted host:port */
-	for (i = 0; i < options->num_permitted_opens; i++) {
-		oarg = arg = xstrdup(options->permitted_opens[i]);
+	for (i = 0; i < num_opens; i++) {
+		oarg = arg = xstrdup(opens[i]);
 		host = hpdelim(&arg);
 		if (host == NULL)
-			fatal("%s: missing host in PermitOpen", __func__);
+			fatal("%s: missing host in %s", __func__, what);
 		host = cleanhostname(host);
 		if (arg == NULL || ((port = permitopen_port(arg)) < 0))
-			fatal("%s: bad port number in PermitOpen", __func__);
+			fatal("%s: bad port number in %s", __func__, what);
 		/* Send it to channels layer */
-		channel_add_adm_permitted_opens(ssh, host, port);
+		channel_add_permission(ssh, FORWARD_ADM,
+		    where, host, port);
 		free(oarg);
 	}
 }
 
+/*
+ * Inform channels layer of permitopen options from configuration.
+ */
+void
+process_permitopen(struct ssh *ssh, ServerOptions *options)
+{
+	process_permitopen_list(ssh, sPermitOpen,
+	    options->permitted_opens, options->num_permitted_opens);
+	process_permitopen_list(ssh, sPermitRemoteOpen,
+	    options->permitted_remote_opens,
+	    options->num_permitted_remote_opens);
+}
+
 struct connection_info *
 get_connection_info(int populate, int use_dns)
 {
@@ -1144,12 +1176,12 @@ process_server_config_line(ServerOptions *options, char *line,
     const char *filename, int linenum, int *activep,
     struct connection_info *connectinfo)
 {
-	char *cp, **charptr, *arg, *arg2, *p;
+	char *cp, ***chararrayptr, **charptr, *arg, *arg2, *p;
 	int cmdline = 0, *intptr, value, value2, n, port;
 	SyslogFacility *log_facility_ptr;
 	LogLevel *log_level_ptr;
 	ServerOpCodes opcode;
-	u_int i, flags = 0;
+	u_int i, *uintptr, uvalue, flags = 0;
 	size_t len;
 	long long val64;
 	const struct multistate *multistate_ptr;
@@ -1799,36 +1831,49 @@ process_server_config_line(ServerOptions *options, char *line,
 		*activep = value;
 		break;
 
+	case sPermitRemoteOpen:
 	case sPermitOpen:
+		if (opcode == sPermitRemoteOpen) {
+			uintptr = &options->num_permitted_remote_opens;
+			chararrayptr = &options->permitted_remote_opens;
+		} else {
+			uintptr = &options->num_permitted_opens;
+			chararrayptr = &options->permitted_opens;
+		}
 		arg = strdelim(&cp);
 		if (!arg || *arg == '\0')
-			fatal("%s line %d: missing PermitOpen specification",
-			    filename, linenum);
-		value = options->num_permitted_opens;	/* modified later */
+			fatal("%s line %d: missing %s specification",
+			    filename, linenum, lookup_opcode_name(opcode));
+		uvalue = *uintptr;	/* modified later */
 		if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) {
-			if (*activep && value == 0) {
-				options->num_permitted_opens = 1;
-				options->permitted_opens = xcalloc(1,
-				    sizeof(*options->permitted_opens));
-				options->permitted_opens[0] = xstrdup(arg);
+			if (*activep && uvalue == 0) {
+				*uintptr = 1;
+				*chararrayptr = xcalloc(1,
+				    sizeof(**chararrayptr));
+				(*chararrayptr)[0] = xstrdup(arg);
 			}
 			break;
 		}
 		for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) {
 			arg2 = xstrdup(arg);
 			p = hpdelim(&arg);
-			if (p == NULL)
-				fatal("%s line %d: missing host in PermitOpen",
-				    filename, linenum);
+			/* XXX support bare port number for PermitRemoteOpen */
+			if (p == NULL) {
+				fatal("%s line %d: missing host in %s",
+				    filename, linenum,
+				    lookup_opcode_name(opcode));
+			}
 			p = cleanhostname(p);
-			if (arg == NULL || ((port = permitopen_port(arg)) < 0))
-				fatal("%s line %d: bad port number in "
-				    "PermitOpen", filename, linenum);
-			if (*activep && value == 0) {
+			if (arg == NULL ||
+			    ((port = permitopen_port(arg)) < 0)) {
+				fatal("%s line %d: bad port number in %s",
+				    filename, linenum,
+				    lookup_opcode_name(opcode));
+			}
+			if (*activep && uvalue == 0) {
 				array_append(filename, linenum,
-				    "PermitOpen",
-				    &options->permitted_opens,
-				    &options->num_permitted_opens, arg2);
+				    lookup_opcode_name(opcode),
+				    chararrayptr, uintptr, arg2);
 			}
 			free(arg2);
 		}
@@ -2307,17 +2352,6 @@ fmt_intarg(ServerOpCodes code, int val)
 	}
 }
 
-static const char *
-lookup_opcode_name(ServerOpCodes code)
-{
-	u_int i;
-
-	for (i = 0; keywords[i].name != NULL; i++)
-		if (keywords[i].opcode == code)
-			return(keywords[i].name);
-	return "UNKNOWN";
-}
-
 static void
 dump_cfg_int(ServerOpCodes code, int val)
 {
@@ -2562,4 +2596,12 @@ dump_config(ServerOptions *o)
 			printf(" %s", o->permitted_opens[i]);
 	}
 	printf("\n");
+	printf("permitremoteopen");
+	if (o->num_permitted_remote_opens == 0)
+		printf(" any");
+	else {
+		for (i = 0; i < o->num_permitted_remote_opens; i++)
+			printf(" %s", o->permitted_remote_opens[i]);
+	}
+	printf("\n");
 }
diff --git a/servconf.h b/servconf.h
index 6d2553c3..62acd893 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.131 2018/04/13 03:57:26 dtucker Exp $ */
+/* $OpenBSD: servconf.h,v 1.132 2018/06/06 18:22:41 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -32,12 +32,6 @@
 #define PRIVSEP_ON		1
 #define PRIVSEP_NOSANDBOX	2
 
-/* AllowTCPForwarding */
-#define FORWARD_DENY		0
-#define FORWARD_REMOTE		(1)
-#define FORWARD_LOCAL		(1<<1)
-#define FORWARD_ALLOW		(FORWARD_REMOTE|FORWARD_LOCAL)
-
 /* PermitOpen */
 #define PERMITOPEN_ANY		0
 #define PERMITOPEN_NONE		-2
@@ -187,8 +181,10 @@ typedef struct {
 
 	int	permit_tun;
 
-	char   **permitted_opens;
-	u_int   num_permitted_opens; /* May also be one of PERMITOPEN_* */
+	char   **permitted_opens;	/* May also be one of PERMITOPEN_* */
+	u_int   num_permitted_opens;
+	char   **permitted_remote_opens; /* May also be one of PERMITOPEN_* */
+	u_int   num_permitted_remote_opens;
 
 	char   *chroot_directory;
 	char   *revoked_keys_file;
@@ -252,6 +248,8 @@ struct connection_info {
 		M_CP_STRARRAYOPT(accept_env, num_accept_env); \
 		M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
 		M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \
+		M_CP_STRARRAYOPT(permitted_remote_opens, \
+		    num_permitted_remote_opens); \
 	} while (0)
 
 struct connection_info *get_connection_info(int, int);
diff --git a/session.c b/session.c
index 5ceebff5..3a3fd841 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.295 2018/06/01 03:33:53 djm Exp $ */
+/* $OpenBSD: session.c,v 1.296 2018/06/06 18:22:41 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -298,7 +298,7 @@ set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
 
 	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
 		return;
-	channel_clear_permitted_opens(ssh);
+	channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
 	for (i = 0; i < auth_opts->npermitopen; i++) {
 		tmp = cp = xstrdup(auth_opts->permitopen[i]);
 		/* This shouldn't fail as it has already been checked */
@@ -308,7 +308,8 @@ set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
 		if (cp == NULL || (port = permitopen_port(cp)) < 0)
 			fatal("%s: internal error: permitopen port",
 			    __func__);
-		channel_add_permitted_opens(ssh, host, port);
+		channel_add_permission(ssh, FORWARD_USER, FORWARD_LOCAL,
+		    host, port);
 		free(tmp);
 	}
 }
@@ -323,13 +324,21 @@ do_authenticated(struct ssh *ssh, Authctxt *authctxt)
 	/* setup the channel layer */
 	/* XXX - streamlocal? */
 	set_permitopen_from_authopts(ssh, auth_opts);
+
 	if (!auth_opts->permit_port_forwarding_flag ||
-	    options.disable_forwarding ||
-	    (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
-		channel_disable_adm_local_opens(ssh);
-	else
-		channel_permit_all_opens(ssh);
-
+	    options.disable_forwarding) {
+		channel_disable_admin(ssh, FORWARD_LOCAL);
+		channel_disable_admin(ssh, FORWARD_REMOTE);
+	} else {
+		if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
+			channel_disable_admin(ssh, FORWARD_LOCAL);
+		else
+			channel_permit_all(ssh, FORWARD_LOCAL);
+		if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0)
+			channel_disable_admin(ssh, FORWARD_REMOTE);
+		else
+			channel_permit_all(ssh, FORWARD_REMOTE);
+	}
 	auth_debug_send();
 
 	prepare_auth_info_file(authctxt->pw, authctxt->session_info);
diff --git a/ssh.c b/ssh.c
index d25960bc..a8505a0e 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.479 2018/06/01 03:33:53 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.480 2018/06/06 18:22:41 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -1654,10 +1654,10 @@ ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)
 			logit("Allocated port %u for remote forward to %s:%d",
 			    rfwd->allocated_port,
 			    rfwd->connect_host, rfwd->connect_port);
-			channel_update_permitted_opens(ssh,
+			channel_update_permission(ssh,
 			    rfwd->handle, rfwd->allocated_port);
 		} else {
-			channel_update_permitted_opens(ssh, rfwd->handle, -1);
+			channel_update_permission(ssh, rfwd->handle, -1);
 		}
 	}
 

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list