summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-06-06 18:22:41 +0000
committerDamien Miller <djm@mindrot.org>2018-06-07 04:27:20 +1000
commit115063a6647007286cc8ca70abfd2a7585f26ccc (patch)
tree7bd8d46ae55ff7fc1f8699740d2d2e106c3d5fe8
parent7703ae5f5d42eb302ded51705166ff6e19c92892 (diff)
downloadopenssh-git-115063a6647007286cc8ca70abfd2a7585f26ccc.tar.gz
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
-rw-r--r--channels.c477
-rw-r--r--channels.h26
-rw-r--r--mux.c6
-rw-r--r--servconf.c138
-rw-r--r--servconf.h16
-rw-r--r--session.c27
-rw-r--r--ssh.c6
7 files changed, 439 insertions, 257 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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@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);
+}
-static int
-fwd_perm_list_add(struct ssh *ssh, int which,
- const char *host_to_connect, int port_to_connect,
- const char *listen_host, const char *listen_path, int listen_port,
- Channel *downstream)
+/* Returns the forwarding permission list for the specified direction */
+static struct permission_set *
+permission_set_get(struct ssh *ssh, int where)
{
- ForwardPermission **fpl;
- u_int n, *nfpl;
+ struct ssh_channels *sc = ssh->chanctxt;
- switch (which) {
- case FWDPERM_USER:
- fpl = &ssh->chanctxt->permitted_opens;
- nfpl = &ssh->chanctxt->num_permitted_opens;
+ switch (where) {
+ case FORWARD_LOCAL:
+ return &sc->local_perms;
break;
- case FWDPERM_ADMIN:
- fpl = &ssh->chanctxt->permitted_adm_opens;
- nfpl = &ssh->chanctxt->num_adm_permitted_opens;
+ case FORWARD_REMOTE:
+ return &sc->remote_perms;
break;
default:
- fatal("%s: invalid list %d", __func__, which);
+ fatal("%s: invalid forwarding direction %d", __func__, where);
}
+}
- if (*nfpl >= INT_MAX)
- fatal("%s: overflow", __func__);
+/* 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);
- *fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl));
- n = (*nfpl)++;
+ 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
+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)
+{
+ struct permission **permp;
+ u_int n, *npermp;
+
+ permission_set_get_array(ssh, who, where, &permp, &npermp);
+
+ if (*npermp >= INT_MAX)
+ fatal("%s: %s overflow", __func__, fwd_ident(who, where));
+
+ *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_permitted_opens(struct ssh *ssh, char *host, int port)
+channel_add_permission(struct ssh *ssh, int who, int where,
+ char *host, int port)
{
- struct ssh_channels *sc = ssh->chanctxt;
+ int local = where == FORWARD_LOCAL;
+ struct permission_set *pset = permission_set_get(ssh, where);
- 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;
+ 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_clear_permission(struct ssh *ssh, int who, int where)
+{
+ struct permission **permp;
+ u_int *npermp;
+
+ 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;
-
- 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)
+ struct permission *perm;
+
+ 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@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@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@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@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@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);
+ if (!auth_opts->permit_port_forwarding_flag ||
+ 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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@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);
}
}