[PATCH] Add "permitlisten" support for -R style forwards
Philipp Heckel
philipp.heckel at gmail.com
Mon May 8 05:17:31 AEST 2017
Hi there,
this patch adds support for per-key restriction of -R style forwards
via a "permitlisten"-option in the authorized_keys file -- similar to
the "permitopen"-option for -L style forwards.
This is desirable if you want to have restricted accounts/keys that
can only be used for -R style forwards on certain ports.
With this example authorized_keys file:
restrict,permitlisten="localhost:8080" ssh-rsa AAAAB3Nza...
This is allowed:
$ ssh -R 8080:localhost:80 root at localhost -N
While this is not allowed (note port 8081):
$ ssh -R 8081:localhost:80 root at localhost -N
Error: remote port forwarding failed for listen port 8081
This is a preliminary patch (no support for a servconf option
"PermitListen" yet), because I wanted to get early feedback before
continuing.
Do you think this approach is correct? Would this be a desirable
feature? Is "permitlisten" the correct name for this? Or would
"permitropen", "permitremoteopen" be better suited?
My WIP branch/pull can also be found here:
https://github.com/openssh/openssh-portable/pull/65
Best,
Philipp Heckel
---
auth-options.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
channels.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
channels.h | 3 +++
serverloop.c | 5 +++--
4 files changed, 128 insertions(+), 2 deletions(-)
diff --git a/auth-options.c b/auth-options.c
index 57b49f7..61034be 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -82,6 +82,7 @@ auth_clear_options(void)
authorized_principals = NULL;
forced_tun_device = -1;
channel_clear_permitted_opens();
+ channel_clear_permitted_listens();
}
/*
@@ -383,6 +384,61 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
free(patterns);
goto next_option;
}
+ cp = "permitlisten=\"";
+ if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+ char *host, *p;
+ int port;
+ char *patterns = xmalloc(strlen(opts) + 1);
+
+ opts += strlen(cp);
+ i = 0;
+ while (*opts) {
+ if (*opts == '"')
+ break;
+ if (*opts == '\\' && opts[1] == '"') {
+ opts += 2;
+ patterns[i++] = '"';
+ continue;
+ }
+ patterns[i++] = *opts++;
+ }
+ if (!*opts) {
+ debug("%.100s, line %lu: missing end quote",
+ file, linenum);
+ auth_debug_add("%.100s, line %lu: missing "
+ "end quote", file, linenum);
+ free(patterns);
+ goto bad_option;
+ }
+ patterns[i] = '\0';
+ opts++;
+ p = patterns;
+ /* XXX - add streamlocal support */
+ host = hpdelim(&p);
+ if (host == NULL || strlen(host) >= NI_MAXHOST) {
+ debug("%.100s, line %lu: Bad permitlisten "
+ "specification <%.100s>", file, linenum,
+ patterns);
+ auth_debug_add("%.100s, line %lu: "
+ "Bad permitlisten specification", file,
+ linenum);
+ free(patterns);
+ goto bad_option;
+ }
+ host = cleanhostname(host);
+ if (p == NULL || (port = permitopen_port(p)) < 0) {
+ debug("%.100s, line %lu: Bad permitlisten port "
+ "<%.100s>", file, linenum, p ? p : "");
+ auth_debug_add("%.100s, line %lu: "
+ "Bad permitopen port", file, linenum);
+ free(patterns);
+ goto bad_option;
+ }
+ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0)
+ channel_add_permitted_listens(host, port);
+ free(patterns);
+ goto next_option;
+ }
cp = "tunnel=\"";
if (strncasecmp(opts, cp, strlen(cp)) == 0) {
char *tun = NULL;
diff --git a/channels.c b/channels.c
index 4092a67..551c2f0 100644
--- a/channels.c
+++ b/channels.c
@@ -129,12 +129,18 @@ static ForwardPermission *permitted_opens = NULL;
/* List of all permitted host/port pairs to connect by the admin. */
static ForwardPermission *permitted_adm_opens = NULL;
+/* List of all permitted remote host/port pairs to connect by the user. */
+static ForwardPermission *permitted_listens = NULL;
+
/* Number of permitted host/port pairs in the array permitted by the user. */
static int num_permitted_opens = 0;
/* Number of permitted host/port pair in the array permitted by the admin. */
static int num_adm_permitted_opens = 0;
+/* Number of permitted remote host/port pairs. */
+static int num_permitted_listens = 0;
+
/* special-case port number meaning allow any port */
#define FWD_PERMIT_ANY_PORT 0
@@ -148,6 +154,10 @@ static int num_adm_permitted_opens = 0;
*/
static int all_opens_permitted = 0;
+/**
+ * If this is true, all remote opens are permitted.
+ */
+static int all_listens_permitted = 0;
/* -- X11 forwarding */
@@ -3503,6 +3513,23 @@ channel_add_permitted_opens(char *host, int port)
all_opens_permitted = 0;
}
+void
+channel_add_permitted_listens(char *host, int port)
+{
+ debug("allow remote port forwarding to host %s port %d", host, port);
+
+ permitted_listens = xreallocarray(permitted_listens,
+ num_permitted_listens + 1, sizeof(*permitted_listens));
+ permitted_listens[num_permitted_listens].host_to_connect = xstrdup(host);
+ permitted_listens[num_permitted_listens].port_to_connect = port;
+ permitted_listens[num_permitted_listens].listen_host = NULL;
+ permitted_listens[num_permitted_listens].listen_path = NULL;
+ permitted_listens[num_permitted_listens].listen_port = 0;
+ num_permitted_listens++;
+
+ all_listens_permitted = 0;
+}
+
/*
* Update the listen port for a dynamic remote forward, after
* the actual 'newport' has been allocated. If 'newport' < 0 is
@@ -3592,6 +3619,21 @@ channel_clear_adm_permitted_opens(void)
}
void
+channel_clear_permitted_listens(void)
+{
+ int i;
+
+ for (i = 0; i < num_permitted_listens; i++) {
+ free(permitted_listens[i].host_to_connect);
+ free(permitted_listens[i].listen_host);
+ free(permitted_listens[i].listen_path);
+ }
+ free(permitted_listens);
+ permitted_listens = NULL;
+ num_permitted_listens = 0;
+}
+
+void
channel_print_adm_permitted_opens(void)
{
int i;
@@ -3885,6 +3927,30 @@ channel_connect_to_path(const char *path, char *ctype, char *rname)
return connect_to(path, PORT_STREAMLOCAL, ctype, rname);
}
+/* Check if connecting to that port is permitted and connect. */
+int
+channel_connect_check_permitted_listens(const char *host, u_short port)
+{
+ int i, permit = 1;
+
+ permit = all_listens_permitted;
+ if (!permit) {
+ for (i = 0; i < num_permitted_listens; i++)
+ if (open_match(&permitted_listens[i], host, port)) {
+ permit = 1;
+ break;
+ }
+ }
+
+ if (!permit) {
+ logit("Received request for remote forward to host %.100s port %d, "
+ "but the request was denied.", host, port);
+ return -1;
+ }
+
+ return 0;
+}
+
void
channel_send_window_changes(void)
{
diff --git a/channels.h b/channels.h
index 4e9b77d..7d55055 100644
--- a/channels.h
+++ b/channels.h
@@ -267,15 +267,18 @@ struct ForwardOptions;
void channel_set_af(int af);
void channel_permit_all_opens(void);
void channel_add_permitted_opens(char *, int);
+void channel_add_permitted_listens(char *, int);
int channel_add_adm_permitted_opens(char *, int);
void channel_disable_adm_local_opens(void);
void channel_update_permitted_opens(int, int);
void channel_clear_permitted_opens(void);
void channel_clear_adm_permitted_opens(void);
+void channel_clear_permitted_listens(void);
void channel_print_adm_permitted_opens(void);
Channel *channel_connect_to_port(const char *, u_short, char *, char *, int *,
const char **);
Channel *channel_connect_to_path(const char *, char *, char *);
+int channel_connect_check_permitted_listens(const char *host, u_short port);
Channel *channel_connect_stdio_fwd(const char*, u_short, int, int);
Channel *channel_connect_by_listen_address(const char *, u_short,
char *, char *);
diff --git a/serverloop.c b/serverloop.c
index 2976f55..50d8feb 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -729,11 +729,12 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
fwd.listen_host, fwd.listen_port);
/* check permissions */
- if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
+ if (channel_connect_check_permitted_listens(fwd.listen_host, fwd.listen_port) < 0 &&
+ ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
no_port_forwarding_flag || options.disable_forwarding ||
(!want_reply && fwd.listen_port == 0) ||
(fwd.listen_port != 0 &&
- !bind_permitted(fwd.listen_port, pw->pw_uid))) {
+ !bind_permitted(fwd.listen_port, pw->pw_uid)))) {
success = 0;
packet_send_debug("Server has disabled port forwarding.");
} else {
--
2.7.4
More information about the openssh-unix-dev
mailing list