summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/ssh/auth-options.c28
-rw-r--r--usr.bin/ssh/channels.c82
-rw-r--r--usr.bin/ssh/channels.h10
-rw-r--r--usr.bin/ssh/clientloop.c49
-rw-r--r--usr.bin/ssh/compat.c20
-rw-r--r--usr.bin/ssh/compat.h3
-rw-r--r--usr.bin/ssh/misc.c44
-rw-r--r--usr.bin/ssh/misc.h3
-rw-r--r--usr.bin/ssh/readconf.c157
-rw-r--r--usr.bin/ssh/readconf.h14
-rw-r--r--usr.bin/ssh/servconf.c65
-rw-r--r--usr.bin/ssh/ssh.159
-rw-r--r--usr.bin/ssh/ssh.c110
-rw-r--r--usr.bin/ssh/ssh_config.570
-rw-r--r--usr.bin/ssh/sshd_config.515
15 files changed, 516 insertions, 213 deletions
diff --git a/usr.bin/ssh/auth-options.c b/usr.bin/ssh/auth-options.c
index 0e146ab1586..04d12d66e48 100644
--- a/usr.bin/ssh/auth-options.c
+++ b/usr.bin/ssh/auth-options.c
@@ -10,7 +10,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: auth-options.c,v 1.28 2003/06/02 09:17:34 markus Exp $");
+RCSID("$OpenBSD: auth-options.c,v 1.29 2005/03/01 10:09:52 djm Exp $");
#include "xmalloc.h"
#include "match.h"
@@ -217,7 +217,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
}
cp = "permitopen=\"";
if (strncasecmp(opts, cp, strlen(cp)) == 0) {
- char host[256], sport[6];
+ char *host, *p;
u_short port;
char *patterns = xmalloc(strlen(opts) + 1);
@@ -236,25 +236,29 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
if (!*opts) {
debug("%.100s, line %lu: missing end quote",
file, linenum);
- auth_debug_add("%.100s, line %lu: missing end quote",
- file, linenum);
+ auth_debug_add("%.100s, line %lu: missing "
+ "end quote", file, linenum);
xfree(patterns);
goto bad_option;
}
patterns[i] = 0;
opts++;
- if (sscanf(patterns, "%255[^:]:%5[0-9]", host, sport) != 2 &&
- sscanf(patterns, "%255[^/]/%5[0-9]", host, sport) != 2) {
- debug("%.100s, line %lu: Bad permitopen specification "
- "<%.100s>", file, linenum, patterns);
+ p = patterns;
+ host = hpdelim(&p);
+ if (host == NULL || strlen(host) >= NI_MAXHOST) {
+ debug("%.100s, line %lu: Bad permitopen "
+ "specification <%.100s>", file, linenum,
+ patterns);
auth_debug_add("%.100s, line %lu: "
- "Bad permitopen specification", file, linenum);
+ "Bad permitopen specification", file,
+ linenum);
xfree(patterns);
goto bad_option;
}
- if ((port = a2port(sport)) == 0) {
- debug("%.100s, line %lu: Bad permitopen port <%.100s>",
- file, linenum, sport);
+ host = cleanhostname(host);
+ if (p == NULL || (port = a2port(p)) == 0) {
+ debug("%.100s, line %lu: Bad permitopen port "
+ "<%.100s>", file, linenum, p ? p : "");
auth_debug_add("%.100s, line %lu: "
"Bad permitopen port", file, linenum);
xfree(patterns);
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c
index da744e84bad..6ec06186165 100644
--- a/usr.bin/ssh/channels.c
+++ b/usr.bin/ssh/channels.c
@@ -39,7 +39,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.211 2004/10/29 21:47:15 djm Exp $");
+RCSID("$OpenBSD: channels.c,v 1.212 2005/03/01 10:09:52 djm Exp $");
#include "ssh.h"
#include "ssh1.h"
@@ -2173,14 +2173,14 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
Channel *c;
- int success, sock, on = 1;
+ int sock, r, success = 0, on = 1, wildcard = 0, is_client;
struct addrinfo hints, *ai, *aitop;
- const char *host;
+ const char *host, *addr;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- success = 0;
host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
listen_addr : host_to_connect;
+ is_client = (type == SSH_CHANNEL_PORT_LISTENER);
if (host == NULL) {
error("No forward host name.");
@@ -2192,16 +2192,60 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
}
/*
+ * 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) ||
+ *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
+ (!is_client && gateway_ports == 1))
+ wildcard = 1;
+ else if (strcmp(listen_addr, "localhost") != 0)
+ addr = listen_addr;
+ }
+
+ debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
+ type, wildcard, (addr == NULL) ? "NULL" : addr);
+
+ /*
* getaddrinfo returns a loopback address if the hostname is
* set to NULL and hints.ai_flags is not AI_PASSIVE
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = IPv4or6;
- hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
+ hints.ai_flags = wildcard ? AI_PASSIVE : 0;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%d", listen_port);
- if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
- packet_disconnect("getaddrinfo: fatal error");
+ if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
+ if (addr == NULL) {
+ /* This really shouldn't happen */
+ packet_disconnect("getaddrinfo: fatal error: %s",
+ gai_strerror(r));
+ } else {
+ verbose("channel_setup_fwd_listener: "
+ "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
+ packet_send_debug("channel_setup_fwd_listener: "
+ "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
+ }
+ aitop = NULL;
+ }
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
@@ -2280,11 +2324,12 @@ channel_cancel_rport_listener(const char *host, u_short port)
/* protocol local port fwd, used by ssh (and sshd in v1) */
int
-channel_setup_local_fwd_listener(u_short listen_port,
+channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
- NULL, listen_port, host_to_connect, port_to_connect, gateway_ports);
+ listen_host, listen_port, host_to_connect, port_to_connect,
+ gateway_ports);
}
/* protocol v2 remote port fwd, used by sshd */
@@ -2302,7 +2347,7 @@ channel_setup_remote_fwd_listener(const char *listen_address,
*/
void
-channel_request_remote_forwarding(u_short listen_port,
+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;
@@ -2313,7 +2358,14 @@ channel_request_remote_forwarding(u_short listen_port,
/* Send the forward request to the remote side. */
if (compat20) {
- const char *address_to_bind = "0.0.0.0";
+ const char *address_to_bind;
+ if (listen_host == NULL)
+ address_to_bind = "localhost";
+ else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0)
+ 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 */
@@ -2359,10 +2411,9 @@ channel_request_remote_forwarding(u_short listen_port,
* local side.
*/
void
-channel_request_rforward_cancel(u_short port)
+channel_request_rforward_cancel(const char *host, u_short port)
{
int i;
- const char *address_to_bind = "0.0.0.0";
if (!compat20)
return;
@@ -2379,7 +2430,7 @@ channel_request_rforward_cancel(u_short port)
packet_start(SSH2_MSG_GLOBAL_REQUEST);
packet_put_cstring("cancel-tcpip-forward");
packet_put_char(0);
- packet_put_cstring(address_to_bind);
+ packet_put_cstring(host == NULL ? "" : host);
packet_put_int(port);
packet_send();
@@ -2418,7 +2469,8 @@ channel_input_port_forward_request(int is_root, int gateway_ports)
packet_disconnect("Dynamic forwarding denied.");
/* Initiate forwarding */
- channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports);
+ channel_setup_local_fwd_listener(NULL, port, hostname,
+ host_port, gateway_ports);
/* Free the argument string. */
xfree(hostname);
diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h
index 977ddfe8541..fed481f3997 100644
--- a/usr.bin/ssh/channels.h
+++ b/usr.bin/ssh/channels.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.75 2004/10/29 21:47:15 djm Exp $ */
+/* $OpenBSD: channels.h,v 1.76 2005/03/01 10:09:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -202,9 +202,11 @@ void channel_clear_permitted_opens(void);
void channel_input_port_forward_request(int, int);
int channel_connect_to(const char *, u_short);
int channel_connect_by_listen_address(u_short);
-void channel_request_remote_forwarding(u_short, const char *, u_short);
-void channel_request_rforward_cancel(u_short port);
-int channel_setup_local_fwd_listener(u_short, const char *, u_short, int);
+void channel_request_remote_forwarding(const char *, u_short,
+ const char *, u_short);
+int channel_setup_local_fwd_listener(const char *, u_short,
+ const char *, u_short, int);
+void channel_request_rforward_cancel(const char *host, u_short port);
int channel_setup_remote_fwd_listener(const char *, u_short, int);
int channel_cancel_rport_listener(const char *, u_short);
diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c
index 033a98a5b97..1e250883f18 100644
--- a/usr.bin/ssh/clientloop.c
+++ b/usr.bin/ssh/clientloop.c
@@ -59,7 +59,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.134 2004/11/07 00:01:46 djm Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.135 2005/03/01 10:09:52 djm Exp $");
#include "ssh.h"
#include "ssh1.h"
@@ -763,11 +763,11 @@ static void
process_cmdline(void)
{
void (*handler)(int);
- char *s, *cmd;
- u_short fwd_port, fwd_host_port;
- char buf[1024], sfwd_port[6], sfwd_host_port[6];
+ char *s, *cmd, *cancel_host;
int delete = 0;
int local = 0;
+ u_short cancel_port;
+ Forward fwd;
leave_raw_mode();
handler = signal(SIGINT, SIG_IGN);
@@ -813,37 +813,38 @@ process_cmdline(void)
s++;
if (delete) {
- if (sscanf(s, "%5[0-9]", sfwd_host_port) != 1) {
- logit("Bad forwarding specification.");
- goto out;
+ cancel_port = 0;
+ cancel_host = hpdelim(&s); /* may be NULL */
+ if (s != NULL) {
+ cancel_port = a2port(s);
+ cancel_host = cleanhostname(cancel_host);
+ } else {
+ cancel_port = a2port(cancel_host);
+ cancel_host = NULL;
}
- if ((fwd_host_port = a2port(sfwd_host_port)) == 0) {
- logit("Bad forwarding port(s).");
+ if (cancel_port == 0) {
+ logit("Bad forwarding close port");
goto out;
}
- channel_request_rforward_cancel(fwd_host_port);
+ channel_request_rforward_cancel(cancel_host, cancel_port);
} else {
- if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]",
- sfwd_port, buf, sfwd_host_port) != 3 &&
- sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]",
- sfwd_port, buf, sfwd_host_port) != 3) {
+ if (!parse_forward(&fwd, s)) {
logit("Bad forwarding specification.");
goto out;
}
- if ((fwd_port = a2port(sfwd_port)) == 0 ||
- (fwd_host_port = a2port(sfwd_host_port)) == 0) {
- logit("Bad forwarding port(s).");
- goto out;
- }
if (local) {
- if (channel_setup_local_fwd_listener(fwd_port, buf,
- fwd_host_port, options.gateway_ports) < 0) {
+ if (channel_setup_local_fwd_listener(fwd.listen_host,
+ fwd.listen_port, fwd.connect_host,
+ fwd.connect_port, options.gateway_ports) < 0) {
logit("Port forwarding failed.");
goto out;
}
- } else
- channel_request_remote_forwarding(fwd_port, buf,
- fwd_host_port);
+ } else {
+ channel_request_remote_forwarding(fwd.listen_host,
+ fwd.listen_port, fwd.connect_host,
+ fwd.connect_port);
+ }
+
logit("Forwarding port.");
}
diff --git a/usr.bin/ssh/compat.c b/usr.bin/ssh/compat.c
index 2fdebe7fa21..4086e853ed6 100644
--- a/usr.bin/ssh/compat.c
+++ b/usr.bin/ssh/compat.c
@@ -23,7 +23,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: compat.c,v 1.70 2003/11/02 11:01:03 markus Exp $");
+RCSID("$OpenBSD: compat.c,v 1.71 2005/03/01 10:09:52 djm Exp $");
#include "buffer.h"
#include "packet.h"
@@ -62,24 +62,28 @@ compat_datafellows(const char *version)
"OpenSSH_2.1*,"
"OpenSSH_2.2*", SSH_OLD_SESSIONID|SSH_BUG_BANNER|
SSH_OLD_DHGEX|SSH_BUG_NOREKEY|
- SSH_BUG_EXTEOF},
+ SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR},
{ "OpenSSH_2.3.0*", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES|
SSH_OLD_DHGEX|SSH_BUG_NOREKEY|
- SSH_BUG_EXTEOF},
+ SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR},
{ "OpenSSH_2.3.*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX|
- SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+ SSH_BUG_NOREKEY|SSH_BUG_EXTEOF|
+ SSH_OLD_FORWARD_ADDR},
{ "OpenSSH_2.5.0p1*,"
"OpenSSH_2.5.1p1*",
SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX|
- SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+ SSH_BUG_NOREKEY|SSH_BUG_EXTEOF|
+ SSH_OLD_FORWARD_ADDR},
{ "OpenSSH_2.5.0*,"
"OpenSSH_2.5.1*,"
"OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY|
- SSH_BUG_EXTEOF},
- { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+ SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR},
+ { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF|
+ SSH_OLD_FORWARD_ADDR},
{ "OpenSSH_2.*,"
"OpenSSH_3.0*,"
- "OpenSSH_3.1*", SSH_BUG_EXTEOF},
+ "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR},
+ { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR },
{ "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
{ "OpenSSH*", 0 },
{ "*MindTerm*", 0 },
diff --git a/usr.bin/ssh/compat.h b/usr.bin/ssh/compat.h
index 5efb5c29e39..cf92dbdeefc 100644
--- a/usr.bin/ssh/compat.h
+++ b/usr.bin/ssh/compat.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.h,v 1.38 2004/07/11 17:48:47 deraadt Exp $ */
+/* $OpenBSD: compat.h,v 1.39 2005/03/01 10:09:52 djm Exp $ */
/*
* Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
@@ -55,6 +55,7 @@
#define SSH_BUG_EXTEOF 0x00200000
#define SSH_BUG_PROBE 0x00400000
#define SSH_BUG_FIRSTKEX 0x00800000
+#define SSH_OLD_FORWARD_ADDR 0x01000000
void enable_compat13(void);
void enable_compat20(void);
diff --git a/usr.bin/ssh/misc.c b/usr.bin/ssh/misc.c
index 98c62348b9f..70ac5eb4edc 100644
--- a/usr.bin/ssh/misc.c
+++ b/usr.bin/ssh/misc.c
@@ -23,7 +23,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: misc.c,v 1.27 2004/12/11 01:48:56 dtucker Exp $");
+RCSID("$OpenBSD: misc.c,v 1.28 2005/03/01 10:09:52 djm Exp $");
#include "misc.h"
#include "log.h"
@@ -269,6 +269,48 @@ convtime(const char *s)
return total;
}
+/*
+ * Search for next delimiter between hostnames/addresses and ports.
+ * Argument may be modified (for termination).
+ * Returns *cp if parsing succeeds.
+ * *cp is set to the start of the next delimiter, if one was found.
+ * If this is the last field, *cp is set to NULL.
+ */
+char *
+hpdelim(char **cp)
+{
+ char *s, *old;
+
+ if (cp == NULL || *cp == NULL)
+ return NULL;
+
+ old = s = *cp;
+ if (*s == '[') {
+ if ((s = strchr(s, ']')) == NULL)
+ return NULL;
+ else
+ s++;
+ } else if ((s = strpbrk(s, ":/")) == NULL)
+ s = *cp + strlen(*cp); /* skip to end (see first case below) */
+
+ switch (*s) {
+ case '\0':
+ *cp = NULL; /* no more fields*/
+ break;
+
+ case ':':
+ case '/':
+ *s = '\0'; /* terminate */
+ *cp = s + 1;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return old;
+}
+
char *
cleanhostname(char *host)
{
diff --git a/usr.bin/ssh/misc.h b/usr.bin/ssh/misc.h
index 193216fa942..8bbc87f0dbd 100644
--- a/usr.bin/ssh/misc.h
+++ b/usr.bin/ssh/misc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.20 2004/12/11 01:48:56 dtucker Exp $ */
+/* $OpenBSD: misc.h,v 1.21 2005/03/01 10:09:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -20,6 +20,7 @@ int set_nonblock(int);
int unset_nonblock(int);
void set_nodelay(int);
int a2port(const char *);
+char *hpdelim(char **);
char *cleanhostname(char *);
char *colon(char *);
long convtime(const char *);
diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c
index 5e567549953..8dddedc4285 100644
--- a/usr.bin/ssh/readconf.c
+++ b/usr.bin/ssh/readconf.c
@@ -12,7 +12,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: readconf.c,v 1.134 2004/07/11 17:48:47 deraadt Exp $");
+RCSID("$OpenBSD: readconf.c,v 1.135 2005/03/01 10:09:52 djm Exp $");
#include "ssh.h"
#include "xmalloc.h"
@@ -206,19 +206,21 @@ static struct {
*/
void
-add_local_forward(Options *options, u_short port, const char *host,
- u_short host_port)
+add_local_forward(Options *options, const Forward *newfwd)
{
Forward *fwd;
extern uid_t original_real_uid;
- if (port < IPPORT_RESERVED && original_real_uid != 0)
+ if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0)
fatal("Privileged ports can only be forwarded by root.");
if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION);
fwd = &options->local_forwards[options->num_local_forwards++];
- fwd->port = port;
- fwd->host = xstrdup(host);
- fwd->host_port = host_port;
+
+ fwd->listen_host = (newfwd->listen_host == NULL) ?
+ NULL : xstrdup(newfwd->listen_host);
+ fwd->listen_port = newfwd->listen_port;
+ fwd->connect_host = xstrdup(newfwd->connect_host);
+ fwd->connect_port = newfwd->connect_port;
}
/*
@@ -227,17 +229,19 @@ add_local_forward(Options *options, u_short port, const char *host,
*/
void
-add_remote_forward(Options *options, u_short port, const char *host,
- u_short host_port)
+add_remote_forward(Options *options, const Forward *newfwd)
{
Forward *fwd;
if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
fatal("Too many remote forwards (max %d).",
SSH_MAX_FORWARDS_PER_DIRECTION);
fwd = &options->remote_forwards[options->num_remote_forwards++];
- fwd->port = port;
- fwd->host = xstrdup(host);
- fwd->host_port = host_port;
+
+ fwd->listen_host = (newfwd->listen_host == NULL) ?
+ NULL : xstrdup(newfwd->listen_host);
+ fwd->listen_port = newfwd->listen_port;
+ fwd->connect_host = xstrdup(newfwd->connect_host);
+ fwd->connect_port = newfwd->connect_port;
}
static void
@@ -245,11 +249,15 @@ clear_forwardings(Options *options)
{
int i;
- for (i = 0; i < options->num_local_forwards; i++)
- xfree(options->local_forwards[i].host);
+ for (i = 0; i < options->num_local_forwards; i++) {
+ xfree(options->local_forwards[i].listen_host);
+ xfree(options->local_forwards[i].connect_host);
+ }
options->num_local_forwards = 0;
- for (i = 0; i < options->num_remote_forwards; i++)
- xfree(options->remote_forwards[i].host);
+ for (i = 0; i < options->num_remote_forwards; i++) {
+ xfree(options->remote_forwards[i].listen_host);
+ xfree(options->remote_forwards[i].connect_host);
+ }
options->num_remote_forwards = 0;
}
@@ -282,11 +290,10 @@ process_config_line(Options *options, const char *host,
char *line, const char *filename, int linenum,
int *activep)
{
- char buf[256], *s, **charptr, *endofnumber, *keyword, *arg;
+ char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256];
int opcode, *intptr, value;
size_t len;
- u_short fwd_port, fwd_host_port;
- char sfwd_host_port[6];
+ Forward fwd;
/* Strip trailing whitespace */
for(len = strlen(line) - 1; len > 0; len--) {
@@ -643,30 +650,26 @@ parse_int:
case oLocalForward:
case oRemoteForward:
arg = strdelim(&s);
- if (!arg || *arg == '\0')
+ if (arg == NULL || *arg == '\0')
fatal("%.200s line %d: Missing port argument.",
filename, linenum);
- if ((fwd_port = a2port(arg)) == 0)
- fatal("%.200s line %d: Bad listen port.",
+ arg2 = strdelim(&s);
+ if (arg2 == NULL || *arg2 == '\0')
+ fatal("%.200s line %d: Missing target argument.",
filename, linenum);
- arg = strdelim(&s);
- if (!arg || *arg == '\0')
- fatal("%.200s line %d: Missing second argument.",
- filename, linenum);
- if (sscanf(arg, "%255[^:]:%5[0-9]", buf, sfwd_host_port) != 2 &&
- sscanf(arg, "%255[^/]/%5[0-9]", buf, sfwd_host_port) != 2)
+
+ /* construct a string for parse_forward */
+ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2);
+
+ if (parse_forward(&fwd, fwdarg) == 0)
fatal("%.200s line %d: Bad forwarding specification.",
filename, linenum);
- if ((fwd_host_port = a2port(sfwd_host_port)) == 0)
- fatal("%.200s line %d: Bad forwarding port.",
- filename, linenum);
+
if (*activep) {
if (opcode == oLocalForward)
- add_local_forward(options, fwd_port, buf,
- fwd_host_port);
+ add_local_forward(options, &fwd);
else if (opcode == oRemoteForward)
- add_remote_forward(options, fwd_port, buf,
- fwd_host_port);
+ add_remote_forward(options, &fwd);
}
break;
@@ -675,12 +678,25 @@ parse_int:
if (!arg || *arg == '\0')
fatal("%.200s line %d: Missing port argument.",
filename, linenum);
- fwd_port = a2port(arg);
- if (fwd_port == 0)
+ memset(&fwd, '\0', sizeof(fwd));
+ fwd.connect_host = "socks";
+ fwd.listen_host = hpdelim(&arg);
+ if (fwd.listen_host == NULL ||
+ strlen(fwd.listen_host) >= NI_MAXHOST)
+ fatal("%.200s line %d: Bad forwarding specification.",
+ filename, linenum);
+ if (arg) {
+ fwd.listen_port = a2port(arg);
+ fwd.listen_host = cleanhostname(fwd.listen_host);
+ } else {
+ fwd.listen_port = a2port(fwd.listen_host);
+ fwd.listen_host = "";
+ }
+ if (fwd.listen_port == 0)
fatal("%.200s line %d: Badly formatted port number.",
filename, linenum);
if (*activep)
- add_local_forward(options, fwd_port, "socks", 0);
+ add_local_forward(options, &fwd);
break;
case oClearAllForwardings:
@@ -1043,3 +1059,68 @@ fill_default_options(Options * options)
/* options->host_key_alias should not be set by default */
/* options->preferred_authentications will be set in ssh */
}
+
+/*
+ * parse_forward
+ * parses a string containing a port forwarding specification of the form:
+ * [listenhost:]listenport:connecthost:connectport
+ * returns number of arguments parsed or zero on error
+ */
+int
+parse_forward(Forward *fwd, const char *fwdspec)
+{
+ int i;
+ char *p, *cp, *fwdarg[4];
+
+ memset(fwd, '\0', sizeof(*fwd));
+
+ cp = p = xstrdup(fwdspec);
+
+ /* skip leading spaces */
+ while (*cp && isspace(*cp))
+ cp++;
+
+ for (i = 0; i < 4; ++i)
+ if ((fwdarg[i] = hpdelim(&cp)) == NULL)
+ break;
+
+ /* Check for trailing garbage in 4-arg case*/
+ if (cp != NULL)
+ i = 0; /* failure */
+
+ switch (i) {
+ case 3:
+ fwd->listen_host = NULL;
+ fwd->listen_port = a2port(fwdarg[0]);
+ fwd->connect_host = xstrdup(cleanhostname(fwdarg[1]));
+ fwd->connect_port = a2port(fwdarg[2]);
+ break;
+
+ case 4:
+ fwd->listen_host = xstrdup(cleanhostname(fwdarg[0]));
+ fwd->listen_port = a2port(fwdarg[1]);
+ fwd->connect_host = xstrdup(cleanhostname(fwdarg[2]));
+ fwd->connect_port = a2port(fwdarg[3]);
+ break;
+ default:
+ i = 0; /* failure */
+ }
+
+ xfree(p);
+
+ if (fwd->listen_port == 0 && fwd->connect_port == 0)
+ goto fail_free;
+
+ if (fwd->connect_host != NULL &&
+ strlen(fwd->connect_host) >= NI_MAXHOST)
+ goto fail_free;
+
+ return (i);
+
+ fail_free:
+ if (fwd->connect_host != NULL)
+ xfree(fwd->connect_host);
+ if (fwd->listen_host != NULL)
+ xfree(fwd->listen_host);
+ return (0);
+}
diff --git a/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h
index ded4225857b..03b772a2d5d 100644
--- a/usr.bin/ssh/readconf.h
+++ b/usr.bin/ssh/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.64 2004/07/11 17:48:47 deraadt Exp $ */
+/* $OpenBSD: readconf.h,v 1.65 2005/03/01 10:09:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -21,9 +21,10 @@
/* Data structure for representing a forwarding request. */
typedef struct {
- u_short port; /* Port to forward. */
- char *host; /* Host to connect. */
- u_short host_port; /* Port to connect on host. */
+ char *listen_host; /* Host (address) to listen on. */
+ u_short listen_port; /* Port to forward. */
+ char *connect_host; /* Host to connect. */
+ u_short connect_port; /* Port to connect on connect_host. */
} Forward;
/* Data structure for representing option data. */
@@ -117,11 +118,12 @@ typedef struct {
void initialize_options(Options *);
void fill_default_options(Options *);
int read_config_file(const char *, const char *, Options *, int);
+int parse_forward(Forward *, const char *);
int
process_config_line(Options *, const char *, char *, const char *, int, int *);
-void add_local_forward(Options *, u_short, const char *, u_short);
-void add_remote_forward(Options *, u_short, const char *, u_short);
+void add_local_forward(Options *, const Forward *);
+void add_remote_forward(Options *, const Forward *);
#endif /* READCONF_H */
diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c
index f3e75cf18f6..8264441c012 100644
--- a/usr.bin/ssh/servconf.c
+++ b/usr.bin/ssh/servconf.c
@@ -10,7 +10,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: servconf.c,v 1.138 2004/12/23 23:11:00 djm Exp $");
+RCSID("$OpenBSD: servconf.c,v 1.139 2005/03/01 10:09:52 djm Exp $");
#include "ssh.h"
#include "log.h"
@@ -405,6 +405,7 @@ process_server_config_line(ServerOptions *options, char *line,
char *cp, **charptr, *arg, *p;
int *intptr, value, i, n;
ServerOpCodes opcode;
+ u_short port;
cp = line;
arg = strdelim(&cp);
@@ -471,39 +472,21 @@ parse_time:
case sListenAddress:
arg = strdelim(&cp);
- if (!arg || *arg == '\0' || strncmp(arg, "[]", 2) == 0)
- fatal("%s line %d: missing inet addr.",
+ if (arg == NULL || *arg == '\0')
+ fatal("%s line %d: missing address",
filename, linenum);
- if (*arg == '[') {
- if ((p = strchr(arg, ']')) == NULL)
- fatal("%s line %d: bad ipv6 inet addr usage.",
- filename, linenum);
- arg++;
- memmove(p, p+1, strlen(p+1)+1);
- } else if (((p = strchr(arg, ':')) == NULL) ||
- (strchr(p+1, ':') != NULL)) {
- add_listen_addr(options, arg, 0);
- break;
- }
- if (*p == ':') {
- u_short port;
-
- p++;
- if (*p == '\0')
- fatal("%s line %d: bad inet addr:port usage.",
- filename, linenum);
- else {
- *(p-1) = '\0';
- if ((port = a2port(p)) == 0)
- fatal("%s line %d: bad port number.",
- filename, linenum);
- add_listen_addr(options, arg, port);
- }
- } else if (*p == '\0')
- add_listen_addr(options, arg, 0);
- else
- fatal("%s line %d: bad inet addr usage.",
+ p = hpdelim(&arg);
+ if (p == NULL)
+ fatal("%s line %d: bad address:port usage",
filename, linenum);
+ p = cleanhostname(p);
+ if (arg == NULL)
+ port = 0;
+ else if ((port = a2port(arg)) == 0)
+ fatal("%s line %d: bad port number", filename, linenum);
+
+ add_listen_addr(options, p, port);
+
break;
case sAddressFamily:
@@ -701,7 +684,23 @@ parse_flag:
case sGatewayPorts:
intptr = &options->gateway_ports;
- goto parse_flag;
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing yes/no/clientspecified "
+ "argument.", filename, linenum);
+ value = 0; /* silence compiler */
+ if (strcmp(arg, "clientspecified") == 0)
+ value = 2;
+ else if (strcmp(arg, "yes") == 0)
+ value = 1;
+ else if (strcmp(arg, "no") == 0)
+ value = 0;
+ else
+ fatal("%s line %d: Bad yes/no/clientspecified "
+ "argument: %s", filename, linenum, arg);
+ if (*intptr == -1)
+ *intptr = value;
+ break;
case sUseDNS:
intptr = &options->use_dns;
diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1
index b8a91a160ca..27da08c696a 100644
--- a/usr.bin/ssh/ssh.1
+++ b/usr.bin/ssh/ssh.1
@@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh.1,v 1.199 2004/11/07 17:42:36 jmc Exp $
+.\" $OpenBSD: ssh.1,v 1.200 2005/03/01 10:09:52 djm Exp $
.Dd September 25, 1999
.Dt SSH 1
.Os
@@ -53,13 +53,13 @@
.Op Fl i Ar identity_file
.Oo Fl L Xo
.Sm off
+.Oo Ar bind_address : Oc
.Ar port :
.Ar host :
.Ar hostport
.Sm on
.Xc
.Oc
-.Ek
.Op Fl l Ar login_name
.Op Fl m Ar mac_spec
.Op Fl O Ar ctl_cmd
@@ -69,6 +69,7 @@
.Ek
.Oo Fl R Xo
.Sm off
+.Oo Ar bind_address : Oc
.Ar port :
.Ar host :
.Ar hostport
@@ -570,6 +571,7 @@ configuration files).
Disables forwarding (delegation) of GSSAPI credentials to the server.
.It Fl L Xo
.Sm off
+.Oo Ar bind_address : Oc
.Ar port : host : hostport
.Sm on
.Xc
@@ -577,7 +579,9 @@ Specifies that the given port on the local (client) host is to be
forwarded to the given host and port on the remote side.
This works by allocating a socket to listen to
.Ar port
-on the local side, and whenever a connection is made to this port, the
+on the local side, optionally bound to the specified
+.Ar bind_address .
+Whenever a connection is made to this port, the
connection is forwarded over the secure channel, and a connection is
made to
.Ar host
@@ -585,14 +589,30 @@ port
.Ar hostport
from the remote machine.
Port forwardings can also be specified in the configuration file.
-Only root can forward privileged ports.
IPv6 addresses can be specified with an alternative syntax:
.Sm off
.Xo
+.Oo Ar bind_address / Oc
.Ar port No / Ar host No /
-.Ar hostport .
+.Ar hostport
.Xc
.Sm on
+or by enclosing the address in square brackets.
+Only the superuser can forward privileged ports.
+By default, the local port is bound in accordance with the
+.Cm GatewayPorts
+setting.
+However, an explicit
+.Ar bind_address
+may be used to bind the connection to a specific address.
+The
+.Ar bind_address
+of
+.Dq localhost
+indicates that the listening port be bound for local use only, while an
+empty address or
+.Dq *
+indicates that the port should be available from all interfaces.
.It Fl l Ar login_name
Specifies the user to log in as on the remote machine.
This also may be specified on a per-host basis in the configuration file.
@@ -724,6 +744,7 @@ Quiet mode.
Causes all warning and diagnostic messages to be suppressed.
.It Fl R Xo
.Sm off
+.Oo Ar bind_address : Oc
.Ar port : host : hostport
.Sm on
.Xc
@@ -738,16 +759,34 @@ made to
port
.Ar hostport
from the local machine.
+.Pp
Port forwardings can also be specified in the configuration file.
Privileged ports can be forwarded only when
logging in as root on the remote machine.
-IPv6 addresses can be specified with an alternative syntax:
-.Sm off
+IPv6 addresses can be specified by enclosing the address in square braces or
+using an alternative syntax:
.Xo
-.Ar port No / Ar host No /
-.Ar hostport .
-.Xc
+.Sm off
+.Oo Ar bind_address / Oc
+.Ar host/port/hostport
.Sm on
+.Xc .
+.Pp
+By default, the listening socket on the server will be bound to the loopback
+interface only.
+This may be overriden by specifying a
+.Ar bind_address .
+An empty
+.Ar bind_address ,
+or the address
+.Ql *
+indicates that the remote socket should listen on all interfaces.
+Specifying a remote
+.Ar bind_address
+will only succeed if the server's
+.Cm GatewayPorts
+option is enabled (see
+.Xr sshd_config 5 ).
.It Fl S Ar ctl_path
Specifies the location of a control socket for connection sharing.
Refer to the description of
diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c
index c767dc7840b..68c7741e495 100644
--- a/usr.bin/ssh/ssh.c
+++ b/usr.bin/ssh/ssh.c
@@ -40,7 +40,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.231 2005/02/16 09:56:44 otto Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.232 2005/03/01 10:09:52 djm Exp $");
#include <openssl/evp.h>
#include <openssl/err.h>
@@ -158,9 +158,10 @@ usage(void)
{
fprintf(stderr,
"usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
-" [-D port] [-e escape_char] [-F configfile] [-i identity_file]\n"
-" [-L port:host:hostport] [-l login_name] [-m mac_spec] [-O ctl_cmd]\n"
-" [-o option] [-p port] [-R port:host:hostport] [-S ctl_path]\n"
+" [-D [listen-host:]port] [-e escape_char] [-F configfile]\n"
+" [-i identity_file] [-L [listen-host:]port:host:hostport]\n"
+" [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
+" [-R [listen-host:]port:host:hostport] [-S ctl_path]\n"
" [user@]hostname [command]\n"
);
exit(1);
@@ -178,14 +179,13 @@ int
main(int ac, char **av)
{
int i, opt, exit_status;
- u_short fwd_port, fwd_host_port;
- char sfwd_port[6], sfwd_host_port[6];
char *p, *cp, *line, buf[256];
struct stat st;
struct passwd *pw;
int dummy;
extern int optind, optreset;
extern char *optarg;
+ Forward fwd;
/*
* Save the original real uid. It will be needed later (uid-swapping
@@ -396,39 +396,51 @@ again:
break;
case 'L':
- case 'R':
- if (sscanf(optarg, "%5[0-9]:%255[^:]:%5[0-9]",
- sfwd_port, buf, sfwd_host_port) != 3 &&
- sscanf(optarg, "%5[0-9]/%255[^/]/%5[0-9]",
- sfwd_port, buf, sfwd_host_port) != 3) {
+ if (parse_forward(&fwd, optarg))
+ add_local_forward(&options, &fwd);
+ else {
fprintf(stderr,
- "Bad forwarding specification '%s'\n",
+ "Bad local forwarding specification '%s'\n",
optarg);
- usage();
- /* NOTREACHED */
+ exit(1);
}
- if ((fwd_port = a2port(sfwd_port)) == 0 ||
- (fwd_host_port = a2port(sfwd_host_port)) == 0) {
+ break;
+
+ case 'R':
+ if (parse_forward(&fwd, optarg)) {
+ add_remote_forward(&options, &fwd);
+ } else {
fprintf(stderr,
- "Bad forwarding port(s) '%s'\n", optarg);
+ "Bad remote forwarding specification "
+ "'%s'\n", optarg);
exit(1);
}
- if (opt == 'L')
- add_local_forward(&options, fwd_port, buf,
- fwd_host_port);
- else if (opt == 'R')
- add_remote_forward(&options, fwd_port, buf,
- fwd_host_port);
break;
case 'D':
- fwd_port = a2port(optarg);
- if (fwd_port == 0) {
+ cp = p = xstrdup(optarg);
+ memset(&fwd, '\0', sizeof(fwd));
+ fwd.connect_host = "socks";
+ if ((fwd.listen_host = hpdelim(&cp)) == NULL) {
+ fprintf(stderr, "Bad dynamic forwarding "
+ "specification '%.100s'\n", optarg);
+ exit(1);
+ }
+ if (cp != NULL) {
+ fwd.listen_port = a2port(cp);
+ fwd.listen_host = cleanhostname(fwd.listen_host);
+ } else {
+ fwd.listen_port = a2port(fwd.listen_host);
+ fwd.listen_host = "";
+ }
+
+ if (fwd.listen_port == 0) {
fprintf(stderr, "Bad dynamic port '%s'\n",
optarg);
exit(1);
}
- add_local_forward(&options, fwd_port, "socks", 0);
+ add_local_forward(&options, &fwd);
+ xfree(p);
break;
case 'C':
@@ -831,14 +843,19 @@ ssh_init_forwarding(void)
/* Initiate local TCP/IP port forwardings. */
for (i = 0; i < options.num_local_forwards; i++) {
- debug("Connections to local port %d forwarded to remote address %.200s:%d",
- options.local_forwards[i].port,
- options.local_forwards[i].host,
- options.local_forwards[i].host_port);
+ debug("Local connections to %.200s:%d forwarded to remote "
+ "address %.200s:%d",
+ (options.local_forwards[i].listen_host == NULL) ?
+ (options.gateway_ports ? "*" : "LOCALHOST") :
+ options.local_forwards[i].listen_host,
+ options.local_forwards[i].listen_port,
+ options.local_forwards[i].connect_host,
+ options.local_forwards[i].connect_port);
success += channel_setup_local_fwd_listener(
- options.local_forwards[i].port,
- options.local_forwards[i].host,
- options.local_forwards[i].host_port,
+ options.local_forwards[i].listen_host,
+ options.local_forwards[i].listen_port,
+ options.local_forwards[i].connect_host,
+ options.local_forwards[i].connect_port,
options.gateway_ports);
}
if (i > 0 && success == 0)
@@ -846,14 +863,17 @@ ssh_init_forwarding(void)
/* Initiate remote TCP/IP port forwardings. */
for (i = 0; i < options.num_remote_forwards; i++) {
- debug("Connections to remote port %d forwarded to local address %.200s:%d",
- options.remote_forwards[i].port,
- options.remote_forwards[i].host,
- options.remote_forwards[i].host_port);
+ debug("Remote connections from %.200s:%d forwarded to "
+ "local address %.200s:%d",
+ options.remote_forwards[i].listen_host,
+ options.remote_forwards[i].listen_port,
+ options.remote_forwards[i].connect_host,
+ options.remote_forwards[i].connect_port);
channel_request_remote_forwarding(
- options.remote_forwards[i].port,
- options.remote_forwards[i].host,
- options.remote_forwards[i].host_port);
+ options.remote_forwards[i].listen_host,
+ options.remote_forwards[i].listen_port,
+ options.remote_forwards[i].connect_host,
+ options.remote_forwards[i].connect_port);
}
}
@@ -1029,12 +1049,12 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt)
return;
debug("remote forward %s for: listen %d, connect %s:%d",
type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure",
- options.remote_forwards[i].port,
- options.remote_forwards[i].host,
- options.remote_forwards[i].host_port);
+ options.remote_forwards[i].listen_port,
+ options.remote_forwards[i].connect_host,
+ options.remote_forwards[i].connect_port);
if (type == SSH2_MSG_REQUEST_FAILURE)
- logit("Warning: remote port forwarding failed for listen port %d",
- options.remote_forwards[i].port);
+ logit("Warning: remote port forwarding failed for listen "
+ "port %d", options.remote_forwards[i].listen_port);
}
static void
diff --git a/usr.bin/ssh/ssh_config.5 b/usr.bin/ssh/ssh_config.5
index 8f6d851b4da..6b6cfc5e949 100644
--- a/usr.bin/ssh/ssh_config.5
+++ b/usr.bin/ssh/ssh_config.5
@@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh_config.5,v 1.42 2005/02/28 00:54:10 djm Exp $
+.\" $OpenBSD: ssh_config.5,v 1.43 2005/03/01 10:09:52 djm Exp $
.Dd September 25, 1999
.Dt SSH_CONFIG 5
.Os
@@ -480,12 +480,37 @@ The default is to use the server specified list.
Specifies that a TCP/IP port on the local machine be forwarded over
the secure channel to the specified host and port from the remote machine.
The first argument must be a port number, and the second must be
-.Ar host:port .
-IPv6 addresses can be specified with an alternative syntax:
-.Ar host/port .
-Multiple forwardings may be specified, and additional
-forwardings can be given on the command line.
+.Xo
+.Sm off
+.Oo Ar bind_address : Oc
+.Ar host:port
+.Sm on
+.Xc .
+IPv6 addresses can be specified by enclosing addresses in square brackets or
+by using an alternative syntax:
+.Xo
+.Sm off
+.Oo Ar bind_address / Oc
+.Ar host/port
+.Sm on
+.Xc .
+Multiple forwardings may be specified, and additional forwardings can be
+given on the command line.
Only the superuser can forward privileged ports.
+By default, the local port is bound in accordance with the
+.Cm GatewayPorts
+setting.
+However, an explicit
+.Ar bind_address
+may be used to bind the connection to a specific address.
+The
+.Ar bind_address
+of
+.Dq localhost
+indicates that the listening port be bound for local use only, while an
+empty address or
+.Dq *
+indicates that the port should be available from all interfaces.
.It Cm LogLevel
Gives the verbosity level that is used when logging messages from
.Nm ssh .
@@ -592,12 +617,39 @@ This option applies to protocol version 2 only.
Specifies that a TCP/IP port on the remote machine be forwarded over
the secure channel to the specified host and port from the local machine.
The first argument must be a port number, and the second must be
-.Ar host:port .
-IPv6 addresses can be specified with an alternative syntax:
-.Ar host/port .
+.Xo
+.Sm off
+.Oo Ar bind_address : Oc
+.Ar host:port
+.Sm on
+.Xc .
+IPv6 addresses can be specified by enclosing any addresses in square brackets
+or by using the alternative syntax:
+.Xo
+.Sm off
+.Oo Ar bind_address / Oc
+.Ar host/port
+.Sm on
+.Xc .
Multiple forwardings may be specified, and additional
forwardings can be given on the command line.
Only the superuser can forward privileged ports.
+.Pp
+If the
+.Ar bind_address
+is not specified, the default is to only bind to loopback addresses.
+If the
+.Ar bind_address
+is
+.Ql *
+or an empty string, then the forwarding is requested to listen on all
+interfaces.
+Specifying a remote
+.Ar bind_address
+will only succeed if the server's
+.Cm GatewayPorts
+option is enabled (see
+.Xr sshd_config 5 ).
.It Cm RhostsRSAAuthentication
Specifies whether to try rhosts based authentication with RSA host
authentication.
diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5
index 1160527e7cc..0baf69e84af 100644
--- a/usr.bin/ssh/sshd_config.5
+++ b/usr.bin/ssh/sshd_config.5
@@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: sshd_config.5,v 1.38 2005/01/08 00:41:19 jmc Exp $
+.\" $OpenBSD: sshd_config.5,v 1.39 2005/03/01 10:09:52 djm Exp $
.Dd September 25, 1999
.Dt SSHD_CONFIG 5
.Os
@@ -256,12 +256,15 @@ This prevents other remote hosts from connecting to forwarded ports.
.Cm GatewayPorts
can be used to specify that
.Nm sshd
-should bind remote port forwardings to the wildcard address,
-thus allowing remote hosts to connect to forwarded ports.
-The argument must be
+should allow remote port forwardings to bind to non-loopback addresses, thus
+allowing other hosts to connect.
+The argument may be
+.Dq no
+to force remote port forwardings to be available to the local host only,
.Dq yes
-or
-.Dq no .
+to force remote port forwardings to bind to the wildcard address, or
+.Dq clientspecified
+to allow the client to select the address to which the forwarding is bound.
The default is
.Dq no .
.It Cm GSSAPIAuthentication