mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-26 23:34:55 +02:00
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
This commit is contained in:
parent
7703ae5f5d
commit
115063a664
475
channels.c
475
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>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
@ -82,6 +82,7 @@
|
|||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "authfd.h"
|
#include "authfd.h"
|
||||||
#include "pathnames.h"
|
#include "pathnames.h"
|
||||||
|
#include "match.h"
|
||||||
|
|
||||||
/* -- agent forwarding */
|
/* -- agent forwarding */
|
||||||
#define NUM_SOCKS 10
|
#define NUM_SOCKS 10
|
||||||
@ -97,6 +98,10 @@
|
|||||||
/* Maximum number of fake X11 displays to try. */
|
/* Maximum number of fake X11 displays to try. */
|
||||||
#define MAX_DISPLAYS 1000
|
#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.
|
* 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
|
* 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 */
|
/* XXX: streamlocal wants a path instead of host:port */
|
||||||
/* Overload host_to_connect; we could just make this match Forward */
|
/* Overload host_to_connect; we could just make this match Forward */
|
||||||
/* XXX - can we use listen_host instead of listen_path? */
|
/* XXX - can we use listen_host instead of listen_path? */
|
||||||
typedef struct {
|
struct permission {
|
||||||
char *host_to_connect; /* Connect to 'host'. */
|
char *host_to_connect; /* Connect to 'host'. */
|
||||||
int port_to_connect; /* Connect to 'port'. */
|
int port_to_connect; /* Connect to 'port'. */
|
||||||
char *listen_host; /* Remote side should listen address. */
|
char *listen_host; /* Remote side should listen address. */
|
||||||
char *listen_path; /* Remote side should listen path. */
|
char *listen_path; /* Remote side should listen path. */
|
||||||
int listen_port; /* Remote side should listen port. */
|
int listen_port; /* Remote side should listen port. */
|
||||||
Channel *downstream; /* Downstream mux*/
|
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 */
|
/* Master structure for channels state */
|
||||||
struct ssh_channels {
|
struct ssh_channels {
|
||||||
@ -149,31 +177,8 @@ struct ssh_channels {
|
|||||||
chan_fn **channel_post;
|
chan_fn **channel_post;
|
||||||
|
|
||||||
/* -- tcp forwarding */
|
/* -- tcp forwarding */
|
||||||
|
struct permission_set local_perms;
|
||||||
/* List of all permitted host/port pairs to connect by the user. */
|
struct permission_set remote_perms;
|
||||||
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;
|
|
||||||
|
|
||||||
/* -- X11 forwarding */
|
/* -- X11 forwarding */
|
||||||
|
|
||||||
@ -448,50 +453,95 @@ channel_close_fds(struct ssh *ssh, Channel *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fwd_perm_clear(ForwardPermission *fp)
|
fwd_perm_clear(struct permission *perm)
|
||||||
{
|
{
|
||||||
free(fp->host_to_connect);
|
free(perm->host_to_connect);
|
||||||
free(fp->listen_host);
|
free(perm->listen_host);
|
||||||
free(fp->listen_path);
|
free(perm->listen_path);
|
||||||
bzero(fp, sizeof(*fp));
|
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
|
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 *host_to_connect, int port_to_connect,
|
||||||
const char *listen_host, const char *listen_path, int listen_port,
|
const char *listen_host, const char *listen_path, int listen_port,
|
||||||
Channel *downstream)
|
Channel *downstream)
|
||||||
{
|
{
|
||||||
ForwardPermission **fpl;
|
struct permission **permp;
|
||||||
u_int n, *nfpl;
|
u_int n, *npermp;
|
||||||
|
|
||||||
switch (which) {
|
permission_set_get_array(ssh, who, where, &permp, &npermp);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*nfpl >= INT_MAX)
|
if (*npermp >= INT_MAX)
|
||||||
fatal("%s: overflow", __func__);
|
fatal("%s: %s overflow", __func__, fwd_ident(who, where));
|
||||||
|
|
||||||
*fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl));
|
*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
|
||||||
n = (*nfpl)++;
|
n = (*npermp)++;
|
||||||
#define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
|
#define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
|
||||||
(*fpl)[n].host_to_connect = MAYBE_DUP(host_to_connect);
|
(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
|
||||||
(*fpl)[n].port_to_connect = port_to_connect;
|
(*permp)[n].port_to_connect = port_to_connect;
|
||||||
(*fpl)[n].listen_host = MAYBE_DUP(listen_host);
|
(*permp)[n].listen_host = MAYBE_DUP(listen_host);
|
||||||
(*fpl)[n].listen_path = MAYBE_DUP(listen_path);
|
(*permp)[n].listen_path = MAYBE_DUP(listen_path);
|
||||||
(*fpl)[n].listen_port = listen_port;
|
(*permp)[n].listen_port = listen_port;
|
||||||
(*fpl)[n].downstream = downstream;
|
(*permp)[n].downstream = downstream;
|
||||||
#undef MAYBE_DUP
|
#undef MAYBE_DUP
|
||||||
return (int)n;
|
return (int)n;
|
||||||
}
|
}
|
||||||
@ -500,30 +550,31 @@ static void
|
|||||||
mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
|
mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
ForwardPermission *fp;
|
struct permission_set *pset = &sc->local_perms;
|
||||||
|
struct permission *perm;
|
||||||
int r;
|
int r;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (fp->downstream != c)
|
if (perm->downstream != c)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* cancel on the server, since mux client is gone */
|
/* cancel on the server, since mux client is gone */
|
||||||
debug("channel %d: cleanup remote forward for %s:%u",
|
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 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh,
|
(r = sshpkt_put_cstring(ssh,
|
||||||
"cancel-tcpip-forward")) != 0 ||
|
"cancel-tcpip-forward")) != 0 ||
|
||||||
(r = sshpkt_put_u8(ssh, 0)) != 0 ||
|
(r = sshpkt_put_u8(ssh, 0)) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh,
|
(r = sshpkt_put_cstring(ssh,
|
||||||
channel_rfwd_bind_host(fp->listen_host))) != 0 ||
|
channel_rfwd_bind_host(perm->listen_host))) != 0 ||
|
||||||
(r = sshpkt_put_u32(ssh, fp->listen_port)) != 0 ||
|
(r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0) {
|
(r = sshpkt_send(ssh)) != 0) {
|
||||||
fatal("%s: channel %i: %s", __func__,
|
fatal("%s: channel %i: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
/* Record that connection to this host/port is permitted. */
|
/* 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, (int)listen_port, downstream);
|
||||||
listen_host = NULL;
|
listen_host = NULL;
|
||||||
break;
|
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 */
|
/* protocol v2 remote port fwd, used by sshd */
|
||||||
int
|
int
|
||||||
channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
|
channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
|
||||||
int *allocated_listen_port, struct ForwardOptions *fwd_opts)
|
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) {
|
if (fwd->listen_path != NULL) {
|
||||||
return channel_setup_fwd_listener_streamlocal(ssh,
|
return channel_setup_fwd_listener_streamlocal(ssh,
|
||||||
SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
|
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
|
* Initiate forwarding of connections to port "port" on remote host through
|
||||||
* the secure channel to host:port from local side.
|
* the secure channel to host:port from local side.
|
||||||
* Returns handle (index) for updating the dynamic listen port with
|
* Returns handle (index) for updating the dynamic listen port with
|
||||||
* channel_update_permitted_opens().
|
* channel_update_permission().
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
|
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_host = xstrdup(fwd->listen_host);
|
||||||
listen_port = fwd->listen_port;
|
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,
|
host_to_connect, port_to_connect,
|
||||||
listen_host, listen_path, listen_port, NULL);
|
listen_host, listen_path, listen_port, NULL);
|
||||||
}
|
}
|
||||||
@ -3732,7 +3850,7 @@ channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
open_match(ForwardPermission *allowed_open, const char *requestedhost,
|
open_match(struct permission *allowed_open, const char *requestedhost,
|
||||||
int requestedport)
|
int requestedport)
|
||||||
{
|
{
|
||||||
if (allowed_open->host_to_connect == NULL)
|
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)
|
* and what we've sent to the remote server (channel_rfwd_bind_host)
|
||||||
*/
|
*/
|
||||||
static int
|
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 *requestedhost, u_short requestedport, int translate)
|
||||||
{
|
{
|
||||||
const char *allowed_host;
|
const char *allowed_host;
|
||||||
@ -3775,7 +3893,7 @@ open_listen_match_tcpip(ForwardPermission *allowed_open,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
open_listen_match_streamlocal(ForwardPermission *allowed_open,
|
open_listen_match_streamlocal(struct permission *allowed_open,
|
||||||
const char *requestedpath)
|
const char *requestedpath)
|
||||||
{
|
{
|
||||||
if (allowed_open->host_to_connect == NULL)
|
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)
|
const char *host, u_short port)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
int r;
|
int r;
|
||||||
u_int i;
|
u_int i;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_listen_match_tcpip(fp, host, port, 0))
|
if (open_listen_match_tcpip(perm, host, port, 0))
|
||||||
break;
|
break;
|
||||||
fp = NULL;
|
perm = NULL;
|
||||||
}
|
}
|
||||||
if (fp == NULL) {
|
if (perm == NULL) {
|
||||||
debug("%s: requested forward not found", __func__);
|
debug("%s: requested forward not found", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -3819,7 +3938,7 @@ channel_request_rforward_cancel_tcpip(struct ssh *ssh,
|
|||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
fatal("%s: send cancel: %s", __func__, ssh_err(r));
|
fatal("%s: send cancel: %s", __func__, ssh_err(r));
|
||||||
|
|
||||||
fwd_perm_clear(fp); /* unregister */
|
fwd_perm_clear(perm); /* unregister */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3832,17 +3951,18 @@ static int
|
|||||||
channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
|
channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
int r;
|
int r;
|
||||||
u_int i;
|
u_int i;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_listen_match_streamlocal(fp, path))
|
if (open_listen_match_streamlocal(perm, path))
|
||||||
break;
|
break;
|
||||||
fp = NULL;
|
perm = NULL;
|
||||||
}
|
}
|
||||||
if (fp == NULL) {
|
if (perm == NULL) {
|
||||||
debug("%s: requested forward not found", __func__);
|
debug("%s: requested forward not found", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -3854,7 +3974,7 @@ channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
|
|||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
fatal("%s: send cancel: %s", __func__, ssh_err(r));
|
fatal("%s: send cancel: %s", __func__, ssh_err(r));
|
||||||
|
|
||||||
fwd_perm_clear(fp); /* unregister */
|
fwd_perm_clear(perm); /* unregister */
|
||||||
|
|
||||||
return 0;
|
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
|
* 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.
|
* anyway, and the server has no way to know but to trust the client anyway.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
channel_permit_all_opens(struct ssh *ssh)
|
channel_permit_all(struct ssh *ssh, int where)
|
||||||
{
|
{
|
||||||
if (ssh->chanctxt->num_permitted_opens == 0)
|
struct permission_set *pset = permission_set_get(ssh, where);
|
||||||
ssh->chanctxt->all_opens_permitted = 1;
|
|
||||||
|
if (pset->num_permitted_user == 0)
|
||||||
|
pset->all_permitted = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permit the specified host/port for forwarding.
|
||||||
|
*/
|
||||||
void
|
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);
|
debug("allow %s forwarding to host %s port %d",
|
||||||
fwd_perm_list_add(ssh, FWDPERM_USER, host, port, NULL, NULL, 0, NULL);
|
fwd_ident(who, where), host, port);
|
||||||
sc->all_opens_permitted = 0;
|
/*
|
||||||
|
* 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.
|
* passed then they entry will be invalidated.
|
||||||
*/
|
*/
|
||||||
void
|
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) {
|
if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
|
||||||
debug("%s: index out of range: %d num_permitted_opens %d",
|
debug("%s: index out of range: %d num_permitted_user %d",
|
||||||
__func__, idx, sc->num_permitted_opens);
|
__func__, idx, pset->num_permitted_user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug("%s allowed port %d for forwarding to host %s port %d",
|
debug("%s allowed port %d for forwarding to host %s port %d",
|
||||||
newport > 0 ? "Updating" : "Removing",
|
newport > 0 ? "Updating" : "Removing",
|
||||||
newport,
|
newport,
|
||||||
sc->permitted_opens[idx].host_to_connect,
|
pset->permitted_user[idx].host_to_connect,
|
||||||
sc->permitted_opens[idx].port_to_connect);
|
pset->permitted_user[idx].port_to_connect);
|
||||||
if (newport <= 0)
|
if (newport <= 0)
|
||||||
fwd_perm_clear(&sc->permitted_opens[idx]);
|
fwd_perm_clear(&pset->permitted_user[idx]);
|
||||||
else {
|
else {
|
||||||
sc->permitted_opens[idx].listen_port =
|
pset->permitted_user[idx].listen_port =
|
||||||
(datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
|
(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 */
|
/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
|
||||||
int
|
int
|
||||||
permitopen_port(const char *p)
|
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)
|
u_short listen_port, char *ctype, char *rname)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
u_int i;
|
u_int i;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) {
|
if (open_listen_match_tcpip(perm,
|
||||||
if (fp->downstream)
|
listen_host, listen_port, 1)) {
|
||||||
return fp->downstream;
|
if (perm->downstream)
|
||||||
if (fp->port_to_connect == 0)
|
return perm->downstream;
|
||||||
|
if (perm->port_to_connect == 0)
|
||||||
return rdynamic_connect_prepare(ssh,
|
return rdynamic_connect_prepare(ssh,
|
||||||
ctype, rname);
|
ctype, rname);
|
||||||
return connect_to(ssh,
|
return connect_to(ssh,
|
||||||
fp->host_to_connect, fp->port_to_connect,
|
perm->host_to_connect, perm->port_to_connect,
|
||||||
ctype, rname);
|
ctype, rname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4174,14 +4300,15 @@ channel_connect_by_listen_path(struct ssh *ssh, const char *path,
|
|||||||
char *ctype, char *rname)
|
char *ctype, char *rname)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
u_int i;
|
u_int i;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_listen_match_streamlocal(fp, path)) {
|
if (open_listen_match_streamlocal(perm, path)) {
|
||||||
return connect_to(ssh,
|
return connect_to(ssh,
|
||||||
fp->host_to_connect, fp->port_to_connect,
|
perm->host_to_connect, perm->port_to_connect,
|
||||||
ctype, rname);
|
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)
|
char *ctype, char *rname, int *reason, const char **errmsg)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
struct channel_connect cctx;
|
struct channel_connect cctx;
|
||||||
Channel *c;
|
Channel *c;
|
||||||
u_int i, permit, permit_adm = 1;
|
u_int i, permit, permit_adm = 1;
|
||||||
int sock;
|
int sock;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
permit = sc->all_opens_permitted;
|
permit = pset->all_permitted;
|
||||||
if (!permit) {
|
if (!permit) {
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_match(fp, host, port)) {
|
if (open_match(perm, host, port)) {
|
||||||
permit = 1;
|
permit = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->num_adm_permitted_opens > 0) {
|
if (pset->num_permitted_admin > 0) {
|
||||||
permit_adm = 0;
|
permit_adm = 0;
|
||||||
for (i = 0; i < sc->num_adm_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_admin; i++) {
|
||||||
fp = &sc->permitted_adm_opens[i];
|
perm = &pset->permitted_admin[i];
|
||||||
if (open_match(fp, host, port)) {
|
if (open_match(perm, host, port)) {
|
||||||
permit_adm = 1;
|
permit_adm = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4255,25 +4383,26 @@ channel_connect_to_path(struct ssh *ssh, const char *path,
|
|||||||
char *ctype, char *rname)
|
char *ctype, char *rname)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct permission_set *pset = &sc->local_perms;
|
||||||
u_int i, permit, permit_adm = 1;
|
u_int i, permit, permit_adm = 1;
|
||||||
ForwardPermission *fp;
|
struct permission *perm;
|
||||||
|
|
||||||
permit = sc->all_opens_permitted;
|
permit = pset->all_permitted;
|
||||||
if (!permit) {
|
if (!permit) {
|
||||||
for (i = 0; i < sc->num_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_user; i++) {
|
||||||
fp = &sc->permitted_opens[i];
|
perm = &pset->permitted_user[i];
|
||||||
if (open_match(fp, path, PORT_STREAMLOCAL)) {
|
if (open_match(perm, path, PORT_STREAMLOCAL)) {
|
||||||
permit = 1;
|
permit = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->num_adm_permitted_opens > 0) {
|
if (pset->num_permitted_admin > 0) {
|
||||||
permit_adm = 0;
|
permit_adm = 0;
|
||||||
for (i = 0; i < sc->num_adm_permitted_opens; i++) {
|
for (i = 0; i < pset->num_permitted_admin; i++) {
|
||||||
fp = &sc->permitted_adm_opens[i];
|
perm = &pset->permitted_admin[i];
|
||||||
if (open_match(fp, path, PORT_STREAMLOCAL)) {
|
if (open_match(perm, path, PORT_STREAMLOCAL)) {
|
||||||
permit_adm = 1;
|
permit_adm = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
26
channels.h
26
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>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
@ -63,6 +63,15 @@
|
|||||||
|
|
||||||
#define CHANNEL_CANCEL_PORT_STATIC -1
|
#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 ssh;
|
||||||
struct Channel;
|
struct Channel;
|
||||||
typedef struct Channel Channel;
|
typedef struct Channel Channel;
|
||||||
@ -283,16 +292,11 @@ int channel_find_open(struct ssh *);
|
|||||||
struct Forward;
|
struct Forward;
|
||||||
struct ForwardOptions;
|
struct ForwardOptions;
|
||||||
void channel_set_af(struct ssh *, int af);
|
void channel_set_af(struct ssh *, int af);
|
||||||
void channel_permit_all_opens(struct ssh *);
|
void channel_permit_all(struct ssh *, int);
|
||||||
void channel_add_permitted_opens(struct ssh *, char *, int);
|
void channel_add_permission(struct ssh *, int, int, char *, int);
|
||||||
int channel_add_adm_permitted_opens(struct ssh *, char *, int);
|
void channel_clear_permission(struct ssh *, int, int);
|
||||||
void channel_copy_adm_permitted_opens(struct ssh *,
|
void channel_disable_admin(struct ssh *, int);
|
||||||
const struct fwd_perm_list *);
|
void channel_update_permission(struct ssh *, int, int);
|
||||||
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 *);
|
|
||||||
Channel *channel_connect_to_port(struct ssh *, const char *, u_short,
|
Channel *channel_connect_to_port(struct ssh *, const char *, u_short,
|
||||||
char *, char *, int *, const char **);
|
char *, char *, int *, const char **);
|
||||||
Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *);
|
Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *);
|
||||||
|
6
mux.c
6
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>
|
* 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, MUX_S_REMOTE_PORT);
|
||||||
buffer_put_int(&out, fctx->rid);
|
buffer_put_int(&out, fctx->rid);
|
||||||
buffer_put_int(&out, rfwd->allocated_port);
|
buffer_put_int(&out, rfwd->allocated_port);
|
||||||
channel_update_permitted_opens(ssh, rfwd->handle,
|
channel_update_permission(ssh, rfwd->handle,
|
||||||
rfwd->allocated_port);
|
rfwd->allocated_port);
|
||||||
} else {
|
} else {
|
||||||
buffer_put_int(&out, MUX_S_OK);
|
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;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
if (rfwd->listen_port == 0)
|
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)
|
if (rfwd->listen_path != NULL)
|
||||||
xasprintf(&failmsg, "remote port forwarding failed for "
|
xasprintf(&failmsg, "remote port forwarding failed for "
|
||||||
"listen path %s", rfwd->listen_path);
|
"listen path %s", rfwd->listen_path);
|
||||||
|
168
servconf.c
168
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
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
@ -160,6 +160,7 @@ initialize_server_options(ServerOptions *options)
|
|||||||
options->num_accept_env = 0;
|
options->num_accept_env = 0;
|
||||||
options->permit_tun = -1;
|
options->permit_tun = -1;
|
||||||
options->permitted_opens = NULL;
|
options->permitted_opens = NULL;
|
||||||
|
options->permitted_remote_opens = NULL;
|
||||||
options->adm_forced_command = NULL;
|
options->adm_forced_command = NULL;
|
||||||
options->chroot_directory = NULL;
|
options->chroot_directory = NULL;
|
||||||
options->authorized_keys_command = NULL;
|
options->authorized_keys_command = NULL;
|
||||||
@ -462,7 +463,7 @@ typedef enum {
|
|||||||
sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
|
sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
|
||||||
sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
|
sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
|
||||||
sAcceptEnv, sPermitTunnel,
|
sAcceptEnv, sPermitTunnel,
|
||||||
sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
|
sMatch, sPermitOpen, sPermitRemoteOpen, sForceCommand, sChrootDirectory,
|
||||||
sUsePrivilegeSeparation, sAllowAgentForwarding,
|
sUsePrivilegeSeparation, sAllowAgentForwarding,
|
||||||
sHostCertificate,
|
sHostCertificate,
|
||||||
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
|
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
|
||||||
@ -597,6 +598,7 @@ static struct {
|
|||||||
{ "permituserrc", sPermitUserRC, SSHCFG_ALL },
|
{ "permituserrc", sPermitUserRC, SSHCFG_ALL },
|
||||||
{ "match", sMatch, SSHCFG_ALL },
|
{ "match", sMatch, SSHCFG_ALL },
|
||||||
{ "permitopen", sPermitOpen, SSHCFG_ALL },
|
{ "permitopen", sPermitOpen, SSHCFG_ALL },
|
||||||
|
{ "permitremoteopen", sPermitRemoteOpen, SSHCFG_ALL },
|
||||||
{ "forcecommand", sForceCommand, SSHCFG_ALL },
|
{ "forcecommand", sForceCommand, SSHCFG_ALL },
|
||||||
{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
|
{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
|
||||||
{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
|
{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
|
||||||
@ -632,6 +634,20 @@ static struct {
|
|||||||
{ -1, NULL }
|
{ -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.
|
* Returns the number of the token pointed to by cp or sBadOption.
|
||||||
*/
|
*/
|
||||||
@ -813,42 +829,58 @@ process_queued_listen_addrs(ServerOptions *options)
|
|||||||
options->num_queued_listens = 0;
|
options->num_queued_listens = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inform channels layer of permitopen options for a single forwarding
|
||||||
|
* direction (local/remote).
|
||||||
|
*/
|
||||||
|
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_permission(ssh, FORWARD_ADM, where);
|
||||||
|
if (num_opens == 0)
|
||||||
|
return; /* permit any */
|
||||||
|
|
||||||
|
/* handle keywords: "any" / "none" */
|
||||||
|
if (num_opens == 1 && strcmp(opens[0], "any") == 0)
|
||||||
|
return;
|
||||||
|
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 < num_opens; i++) {
|
||||||
|
oarg = arg = xstrdup(opens[i]);
|
||||||
|
host = hpdelim(&arg);
|
||||||
|
if (host == NULL)
|
||||||
|
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 %s", __func__, what);
|
||||||
|
/* Send it to channels layer */
|
||||||
|
channel_add_permission(ssh, FORWARD_ADM,
|
||||||
|
where, host, port);
|
||||||
|
free(oarg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inform channels layer of permitopen options from configuration.
|
* Inform channels layer of permitopen options from configuration.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
process_permitopen(struct ssh *ssh, ServerOptions *options)
|
process_permitopen(struct ssh *ssh, ServerOptions *options)
|
||||||
{
|
{
|
||||||
u_int i;
|
process_permitopen_list(ssh, sPermitOpen,
|
||||||
int port;
|
options->permitted_opens, options->num_permitted_opens);
|
||||||
char *host, *arg, *oarg;
|
process_permitopen_list(ssh, sPermitRemoteOpen,
|
||||||
|
options->permitted_remote_opens,
|
||||||
channel_clear_adm_permitted_opens(ssh);
|
options->num_permitted_remote_opens);
|
||||||
if (options->num_permitted_opens == 0)
|
|
||||||
return; /* permit any */
|
|
||||||
|
|
||||||
/* handle keywords: "any" / "none" */
|
|
||||||
if (options->num_permitted_opens == 1 &&
|
|
||||||
strcmp(options->permitted_opens[0], "any") == 0)
|
|
||||||
return;
|
|
||||||
if (options->num_permitted_opens == 1 &&
|
|
||||||
strcmp(options->permitted_opens[0], "none") == 0) {
|
|
||||||
channel_disable_adm_local_opens(ssh);
|
|
||||||
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]);
|
|
||||||
host = hpdelim(&arg);
|
|
||||||
if (host == NULL)
|
|
||||||
fatal("%s: missing host in PermitOpen", __func__);
|
|
||||||
host = cleanhostname(host);
|
|
||||||
if (arg == NULL || ((port = permitopen_port(arg)) < 0))
|
|
||||||
fatal("%s: bad port number in PermitOpen", __func__);
|
|
||||||
/* Send it to channels layer */
|
|
||||||
channel_add_adm_permitted_opens(ssh, host, port);
|
|
||||||
free(oarg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct connection_info *
|
struct connection_info *
|
||||||
@ -1144,12 +1176,12 @@ process_server_config_line(ServerOptions *options, char *line,
|
|||||||
const char *filename, int linenum, int *activep,
|
const char *filename, int linenum, int *activep,
|
||||||
struct connection_info *connectinfo)
|
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;
|
int cmdline = 0, *intptr, value, value2, n, port;
|
||||||
SyslogFacility *log_facility_ptr;
|
SyslogFacility *log_facility_ptr;
|
||||||
LogLevel *log_level_ptr;
|
LogLevel *log_level_ptr;
|
||||||
ServerOpCodes opcode;
|
ServerOpCodes opcode;
|
||||||
u_int i, flags = 0;
|
u_int i, *uintptr, uvalue, flags = 0;
|
||||||
size_t len;
|
size_t len;
|
||||||
long long val64;
|
long long val64;
|
||||||
const struct multistate *multistate_ptr;
|
const struct multistate *multistate_ptr;
|
||||||
@ -1799,36 +1831,49 @@ process_server_config_line(ServerOptions *options, char *line,
|
|||||||
*activep = value;
|
*activep = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case sPermitRemoteOpen:
|
||||||
case sPermitOpen:
|
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);
|
arg = strdelim(&cp);
|
||||||
if (!arg || *arg == '\0')
|
if (!arg || *arg == '\0')
|
||||||
fatal("%s line %d: missing PermitOpen specification",
|
fatal("%s line %d: missing %s specification",
|
||||||
filename, linenum);
|
filename, linenum, lookup_opcode_name(opcode));
|
||||||
value = options->num_permitted_opens; /* modified later */
|
uvalue = *uintptr; /* modified later */
|
||||||
if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) {
|
if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) {
|
||||||
if (*activep && value == 0) {
|
if (*activep && uvalue == 0) {
|
||||||
options->num_permitted_opens = 1;
|
*uintptr = 1;
|
||||||
options->permitted_opens = xcalloc(1,
|
*chararrayptr = xcalloc(1,
|
||||||
sizeof(*options->permitted_opens));
|
sizeof(**chararrayptr));
|
||||||
options->permitted_opens[0] = xstrdup(arg);
|
(*chararrayptr)[0] = xstrdup(arg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) {
|
for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) {
|
||||||
arg2 = xstrdup(arg);
|
arg2 = xstrdup(arg);
|
||||||
p = hpdelim(&arg);
|
p = hpdelim(&arg);
|
||||||
if (p == NULL)
|
/* XXX support bare port number for PermitRemoteOpen */
|
||||||
fatal("%s line %d: missing host in PermitOpen",
|
if (p == NULL) {
|
||||||
filename, linenum);
|
fatal("%s line %d: missing host in %s",
|
||||||
|
filename, linenum,
|
||||||
|
lookup_opcode_name(opcode));
|
||||||
|
}
|
||||||
p = cleanhostname(p);
|
p = cleanhostname(p);
|
||||||
if (arg == NULL || ((port = permitopen_port(arg)) < 0))
|
if (arg == NULL ||
|
||||||
fatal("%s line %d: bad port number in "
|
((port = permitopen_port(arg)) < 0)) {
|
||||||
"PermitOpen", filename, linenum);
|
fatal("%s line %d: bad port number in %s",
|
||||||
if (*activep && value == 0) {
|
filename, linenum,
|
||||||
|
lookup_opcode_name(opcode));
|
||||||
|
}
|
||||||
|
if (*activep && uvalue == 0) {
|
||||||
array_append(filename, linenum,
|
array_append(filename, linenum,
|
||||||
"PermitOpen",
|
lookup_opcode_name(opcode),
|
||||||
&options->permitted_opens,
|
chararrayptr, uintptr, arg2);
|
||||||
&options->num_permitted_opens, arg2);
|
|
||||||
}
|
}
|
||||||
free(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
|
static void
|
||||||
dump_cfg_int(ServerOpCodes code, int val)
|
dump_cfg_int(ServerOpCodes code, int val)
|
||||||
{
|
{
|
||||||
@ -2562,4 +2596,12 @@ dump_config(ServerOptions *o)
|
|||||||
printf(" %s", o->permitted_opens[i]);
|
printf(" %s", o->permitted_opens[i]);
|
||||||
}
|
}
|
||||||
printf("\n");
|
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");
|
||||||
}
|
}
|
||||||
|
16
servconf.h
16
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>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
@ -32,12 +32,6 @@
|
|||||||
#define PRIVSEP_ON 1
|
#define PRIVSEP_ON 1
|
||||||
#define PRIVSEP_NOSANDBOX 2
|
#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 */
|
/* PermitOpen */
|
||||||
#define PERMITOPEN_ANY 0
|
#define PERMITOPEN_ANY 0
|
||||||
#define PERMITOPEN_NONE -2
|
#define PERMITOPEN_NONE -2
|
||||||
@ -187,8 +181,10 @@ typedef struct {
|
|||||||
|
|
||||||
int permit_tun;
|
int permit_tun;
|
||||||
|
|
||||||
char **permitted_opens;
|
char **permitted_opens; /* May also be one of PERMITOPEN_* */
|
||||||
u_int num_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 *chroot_directory;
|
||||||
char *revoked_keys_file;
|
char *revoked_keys_file;
|
||||||
@ -252,6 +248,8 @@ struct connection_info {
|
|||||||
M_CP_STRARRAYOPT(accept_env, num_accept_env); \
|
M_CP_STRARRAYOPT(accept_env, num_accept_env); \
|
||||||
M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
|
M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
|
||||||
M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \
|
M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \
|
||||||
|
M_CP_STRARRAYOPT(permitted_remote_opens, \
|
||||||
|
num_permitted_remote_opens); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
struct connection_info *get_connection_info(int, int);
|
struct connection_info *get_connection_info(int, int);
|
||||||
|
27
session.c
27
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
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
* All rights reserved
|
* 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)
|
if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
|
||||||
return;
|
return;
|
||||||
channel_clear_permitted_opens(ssh);
|
channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
|
||||||
for (i = 0; i < auth_opts->npermitopen; i++) {
|
for (i = 0; i < auth_opts->npermitopen; i++) {
|
||||||
tmp = cp = xstrdup(auth_opts->permitopen[i]);
|
tmp = cp = xstrdup(auth_opts->permitopen[i]);
|
||||||
/* This shouldn't fail as it has already been checked */
|
/* 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)
|
if (cp == NULL || (port = permitopen_port(cp)) < 0)
|
||||||
fatal("%s: internal error: permitopen port",
|
fatal("%s: internal error: permitopen port",
|
||||||
__func__);
|
__func__);
|
||||||
channel_add_permitted_opens(ssh, host, port);
|
channel_add_permission(ssh, FORWARD_USER, FORWARD_LOCAL,
|
||||||
|
host, port);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,13 +324,21 @@ do_authenticated(struct ssh *ssh, Authctxt *authctxt)
|
|||||||
/* setup the channel layer */
|
/* setup the channel layer */
|
||||||
/* XXX - streamlocal? */
|
/* XXX - streamlocal? */
|
||||||
set_permitopen_from_authopts(ssh, auth_opts);
|
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();
|
auth_debug_send();
|
||||||
|
|
||||||
prepare_auth_info_file(authctxt->pw, authctxt->session_info);
|
prepare_auth_info_file(authctxt->pw, authctxt->session_info);
|
||||||
|
6
ssh.c
6
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>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* 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",
|
logit("Allocated port %u for remote forward to %s:%d",
|
||||||
rfwd->allocated_port,
|
rfwd->allocated_port,
|
||||||
rfwd->connect_host, rfwd->connect_port);
|
rfwd->connect_host, rfwd->connect_port);
|
||||||
channel_update_permitted_opens(ssh,
|
channel_update_permission(ssh,
|
||||||
rfwd->handle, rfwd->allocated_port);
|
rfwd->handle, rfwd->allocated_port);
|
||||||
} else {
|
} else {
|
||||||
channel_update_permitted_opens(ssh, rfwd->handle, -1);
|
channel_update_permission(ssh, rfwd->handle, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user