diff options
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 252 |
1 files changed, 187 insertions, 65 deletions
diff --git a/channels.c b/channels.c index 24d4a9f4256f2..f6e9b4d8cffb1 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.311 2011/06/22 22:08:42 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.315 2011/09/23 07:45:05 markus Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -125,6 +125,9 @@ 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; +/* special-case port number meaning allow any port */ +#define FWD_PERMIT_ANY_PORT 0 + /* * 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 @@ -302,6 +305,8 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, buffer_init(&c->output); buffer_init(&c->extended); c->path = NULL; + c->listening_addr = NULL; + c->listening_port = 0; c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; c->flags = 0; @@ -411,6 +416,10 @@ channel_free(Channel *c) xfree(c->path); c->path = NULL; } + if (c->listening_addr) { + xfree(c->listening_addr); + c->listening_addr = NULL; + } while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) cc->abandon_cb(c, cc->ctx); @@ -2634,6 +2643,46 @@ channel_set_af(int af) IPv4or6 = af; } + +/* + * Determine whether or not a port forward listens to loopback, the + * specified address or wildcard. On the client, a specified bind + * address will always override gateway_ports. On the server, a + * gateway_ports of 1 (``yes'') will override the client's specification + * and force a wildcard bind, whereas a value of 2 (``clientspecified'') + * will bind to whatever address the client asked for. + * + * Special-case listen_addrs are: + * + * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR + * "" (empty string), "*" -> wildcard v4/v6 + * "localhost" -> loopback v4/v6 + */ +static const char * +channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, + int is_client, int gateway_ports) +{ + const char *addr = NULL; + int wildcard = 0; + + if (listen_addr == NULL) { + /* No address specified: default to gateway_ports setting */ + if (gateway_ports) + wildcard = 1; + } else if (gateway_ports || is_client) { + if (((datafellows & SSH_OLD_FORWARD_ADDR) && + strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || + *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || + (!is_client && gateway_ports == 1)) + wildcard = 1; + else if (strcmp(listen_addr, "localhost") != 0) + addr = listen_addr; + } + if (wildcardp != NULL) + *wildcardp = wildcard; + return addr; +} + static int channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, int *allocated_listen_port, @@ -2659,36 +2708,9 @@ channel_setup_fwd_listener(int type, const char *listen_addr, return 0; } - /* - * Determine whether or not a port forward listens to loopback, - * specified address or wildcard. On the client, a specified bind - * address will always override gateway_ports. On the server, a - * gateway_ports of 1 (``yes'') will override the client's - * specification and force a wildcard bind, whereas a value of 2 - * (``clientspecified'') will bind to whatever address the client - * asked for. - * - * Special-case listen_addrs are: - * - * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR - * "" (empty string), "*" -> wildcard v4/v6 - * "localhost" -> loopback v4/v6 - */ - addr = NULL; - if (listen_addr == NULL) { - /* No address specified: default to gateway_ports setting */ - if (gateway_ports) - wildcard = 1; - } else if (gateway_ports || is_client) { - if (((datafellows & SSH_OLD_FORWARD_ADDR) && - strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || - *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || - (!is_client && gateway_ports == 1)) - wildcard = 1; - else if (strcmp(listen_addr, "localhost") != 0) - addr = listen_addr; - } - + /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ + addr = channel_fwd_bind_addr(listen_addr, &wildcard, + is_client, gateway_ports); debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", type, wildcard, (addr == NULL) ? "NULL" : addr); @@ -2792,7 +2814,12 @@ channel_setup_fwd_listener(int type, const char *listen_addr, 0, "port listener", 1); c->path = xstrdup(host); c->host_port = port_to_connect; - c->listening_port = listen_port; + c->listening_addr = addr == NULL ? NULL : xstrdup(addr); + if (listen_port == 0 && allocated_listen_port != NULL && + !(datafellows & SSH_BUG_DYNAMIC_RPORT)) + c->listening_port = *allocated_listen_port; + else + c->listening_port = listen_port; success = 1; } if (success == 0) @@ -2810,9 +2837,44 @@ channel_cancel_rport_listener(const char *host, u_short port) for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) + continue; + if (strcmp(c->path, host) == 0 && c->listening_port == port) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + +int +channel_cancel_lport_listener(const char *lhost, u_short lport, + int cport, int gateway_ports) +{ + u_int i; + int found = 0; + const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports); - if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && - strcmp(c->path, host) == 0 && c->listening_port == port) { + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) + continue; + if (c->listening_port != lport) + continue; + if (cport == CHANNEL_CANCEL_PORT_STATIC) { + /* skip dynamic forwardings */ + if (c->host_port == 0) + continue; + } else { + if (c->host_port != cport) + continue; + } + if ((c->listening_addr == NULL && addr != NULL) || + (c->listening_addr != NULL && addr == NULL)) + continue; + if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; @@ -2843,37 +2905,44 @@ channel_setup_remote_fwd_listener(const char *listen_address, } /* + * Translate the requested rfwd listen host to something usable for + * this server. + */ +static const char * +channel_rfwd_bind_host(const char *listen_host) +{ + if (listen_host == NULL) { + if (datafellows & SSH_BUG_RFWD_ADDR) + return "127.0.0.1"; + else + return "localhost"; + } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { + if (datafellows & SSH_BUG_RFWD_ADDR) + return "0.0.0.0"; + else + return ""; + } else + return 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(). */ - int channel_request_remote_forwarding(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect) { - int type, success = 0; + int type, success = 0, idx = -1; /* Send the forward request to the remote side. */ if (compat20) { - const char *address_to_bind; - if (listen_host == NULL) { - if (datafellows & SSH_BUG_RFWD_ADDR) - address_to_bind = "127.0.0.1"; - else - address_to_bind = "localhost"; - } else if (*listen_host == '\0' || - strcmp(listen_host, "*") == 0) { - if (datafellows & SSH_BUG_RFWD_ADDR) - address_to_bind = "0.0.0.0"; - else - address_to_bind = ""; - } else - address_to_bind = listen_host; - packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("tcpip-forward"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(address_to_bind); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(channel_rfwd_bind_host(listen_host)); packet_put_int(listen_port); packet_send(); packet_write_wait(); @@ -2905,25 +2974,25 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, /* Record that connection to this host/port is permitted. */ permitted_opens = xrealloc(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); - permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); - permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; - permitted_opens[num_permitted_opens].listen_port = listen_port; - num_permitted_opens++; + idx = num_permitted_opens++; + permitted_opens[idx].host_to_connect = xstrdup(host_to_connect); + permitted_opens[idx].port_to_connect = port_to_connect; + permitted_opens[idx].listen_port = listen_port; } - return (success ? 0 : -1); + return (idx); } /* * Request cancellation of remote forwarding of connection host:port from * local side. */ -void +int channel_request_rforward_cancel(const char *host, u_short port) { int i; if (!compat20) - return; + return -1; for (i = 0; i < num_permitted_opens; i++) { if (permitted_opens[i].host_to_connect != NULL && @@ -2932,12 +3001,12 @@ channel_request_rforward_cancel(const char *host, u_short port) } if (i >= num_permitted_opens) { debug("%s: requested forward not found", __func__); - return; + return -1; } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-tcpip-forward"); packet_put_char(0); - packet_put_cstring(host == NULL ? "" : host); + packet_put_cstring(channel_rfwd_bind_host(host)); packet_put_int(port); packet_send(); @@ -2945,6 +3014,8 @@ channel_request_rforward_cancel(const char *host, u_short port) permitted_opens[i].port_to_connect = 0; xfree(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; + + return 0; } /* @@ -3013,6 +3084,35 @@ channel_add_permitted_opens(char *host, int port) all_opens_permitted = 0; } +/* + * Update the listen port for a dynamic remote forward, after + * the actual 'newport' has been allocated. If 'newport' < 0 is + * passed then they entry will be invalidated. + */ +void +channel_update_permitted_opens(int idx, int newport) +{ + if (idx < 0 || idx >= num_permitted_opens) { + debug("channel_update_permitted_opens: index out of range:" + " %d num_permitted_opens %d", idx, num_permitted_opens); + return; + } + debug("%s allowed port %d for forwarding to host %s port %d", + newport > 0 ? "Updating" : "Removing", + newport, + permitted_opens[idx].host_to_connect, + permitted_opens[idx].port_to_connect); + if (newport >= 0) { + permitted_opens[idx].listen_port = + (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; + } else { + permitted_opens[idx].listen_port = 0; + permitted_opens[idx].port_to_connect = 0; + xfree(permitted_opens[idx].host_to_connect); + permitted_opens[idx].host_to_connect = NULL; + } +} + int channel_add_adm_permitted_opens(char *host, int port) { @@ -3073,6 +3173,28 @@ channel_print_adm_permitted_opens(void) printf("\n"); } +/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ +int +permitopen_port(const char *p) +{ + int port; + + if (strcmp(p, "*") == 0) + return FWD_PERMIT_ANY_PORT; + if ((port = a2port(p)) > 0) + return port; + return -1; +} + +static int +port_match(u_short allowedport, u_short requestedport) +{ + if (allowedport == FWD_PERMIT_ANY_PORT || + allowedport == requestedport) + return 1; + return 0; +} + /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) @@ -3175,7 +3297,7 @@ channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) for (i = 0; i < num_permitted_opens; i++) { if (permitted_opens[i].host_to_connect != NULL && - permitted_opens[i].listen_port == listen_port) { + port_match(permitted_opens[i].listen_port, listen_port)) { return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect, ctype, rname); @@ -3196,7 +3318,7 @@ channel_connect_to(const char *host, u_short port, char *ctype, char *rname) if (!permit) { for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL && - permitted_opens[i].port_to_connect == port && + port_match(permitted_opens[i].port_to_connect, port) && strcmp(permitted_opens[i].host_to_connect, host) == 0) permit = 1; } @@ -3205,7 +3327,7 @@ channel_connect_to(const char *host, u_short port, char *ctype, char *rname) permit_adm = 0; for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL && - permitted_adm_opens[i].port_to_connect == port && + port_match(permitted_adm_opens[i].port_to_connect, port) && strcmp(permitted_adm_opens[i].host_to_connect, host) == 0) permit_adm = 1; |