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