summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2017-10-23 05:08:00 +0000
committerDamien Miller <djm@mindrot.org>2017-10-23 16:14:30 +1100
commitb7548b12a6b2b4abf4d057192c353147e0abba08 (patch)
treedc76477cd371b6197ba840c3a178bfbcf6d7baba
parent887669ef032d63cf07f53cada216fa8a0c9a7d72 (diff)
upstream commit
Expose devices allocated for tun/tap forwarding. At the client, the device may be obtained from a new %T expansion for LocalCommand. At the server, the allocated devices will be listed in a SSH_TUNNEL variable exposed to the environment of any user sessions started after the tunnel forwarding was established. ok markus Upstream-ID: e61e53f8ae80566e9ddc0d67a5df5bdf2f3c9f9e
-rw-r--r--clientloop.c12
-rw-r--r--clientloop.h4
-rw-r--r--misc.c12
-rw-r--r--misc.h4
-rw-r--r--openbsd-compat/port-tun.c16
-rw-r--r--openbsd-compat/port-tun.h2
-rw-r--r--serverloop.c22
-rw-r--r--session.c5
-rw-r--r--ssh.19
-rw-r--r--ssh.c108
-rw-r--r--ssh_config.516
11 files changed, 133 insertions, 77 deletions
diff --git a/clientloop.c b/clientloop.c
index 791d336e..46ede4f0 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.305 2017/09/19 04:24:22 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.306 2017/10/23 05:08:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1601,12 +1601,13 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan)
return c;
}
-int
+char *
client_request_tun_fwd(struct ssh *ssh, int tun_mode,
int local_tun, int remote_tun)
{
Channel *c;
int fd;
+ char *ifname = NULL;
if (tun_mode == SSH_TUNMODE_NO)
return 0;
@@ -1614,10 +1615,11 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);
/* Open local tunnel device */
- if ((fd = tun_open(local_tun, tun_mode)) == -1) {
+ if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) {
error("Tunnel device open failed.");
- return -1;
+ return NULL;
}
+ debug("Tunnel forwarding using interface %s", ifname);
c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
@@ -1638,7 +1640,7 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
packet_put_int(remote_tun);
packet_send();
- return 0;
+ return ifname;
}
/* XXXX move to generic input handler */
diff --git a/clientloop.h b/clientloop.h
index a1975ccc..8d1f0bff 100644
--- a/clientloop.h
+++ b/clientloop.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.h,v 1.34 2017/09/12 06:32:07 djm Exp $ */
+/* $OpenBSD: clientloop.h,v 1.35 2017/10/23 05:08:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -46,7 +46,7 @@ int client_x11_get_proto(struct ssh *, const char *, const char *,
void client_global_request_reply_fwd(int, u_int32_t, void *);
void client_session2_setup(struct ssh *, int, int, int,
const char *, struct termios *, int, Buffer *, char **);
-int client_request_tun_fwd(struct ssh *, int, int, int);
+char *client_request_tun_fwd(struct ssh *, int, int, int);
void client_stop_mux(void);
/* Escape filter for protocol 2 sessions */
diff --git a/misc.c b/misc.c
index d4d0e44a..7f1d4256 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.114 2017/10/21 23:06:24 millert Exp $ */
+/* $OpenBSD: misc.c,v 1.115 2017/10/23 05:08:00 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -964,16 +964,19 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
}
int
-tun_open(int tun, int mode)
+tun_open(int tun, int mode, char **ifname)
{
#if defined(CUSTOM_SYS_TUN_OPEN)
- return (sys_tun_open(tun, mode));
+ return (sys_tun_open(tun, mode, ifname));
#elif defined(SSH_TUN_OPENBSD)
struct ifreq ifr;
char name[100];
int fd = -1, sock;
const char *tunbase = "tun";
+ if (ifname != NULL)
+ *ifname = NULL;
+
if (mode == SSH_TUNMODE_ETHERNET)
tunbase = "tap";
@@ -1020,6 +1023,9 @@ tun_open(int tun, int mode)
}
}
+ if (ifname != NULL)
+ *ifname = xstrdup(ifr.ifr_name);
+
close(sock);
return fd;
diff --git a/misc.h b/misc.h
index b6f502b3..19fdf5c8 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.64 2017/10/21 23:06:24 millert Exp $ */
+/* $OpenBSD: misc.h,v 1.65 2017/10/23 05:08:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -87,7 +87,7 @@ void replacearg(arglist *, u_int, char *, ...)
__attribute__((format(printf, 3, 4)));
void freeargs(arglist *);
-int tun_open(int, int);
+int tun_open(int, int, char **);
/* Common definitions for ssh tunnel device forwarding */
#define SSH_TUNMODE_NO 0x00
diff --git a/openbsd-compat/port-tun.c b/openbsd-compat/port-tun.c
index 7579c608..0e75c911 100644
--- a/openbsd-compat/port-tun.c
+++ b/openbsd-compat/port-tun.c
@@ -56,12 +56,15 @@
#include <linux/if_tun.h>
int
-sys_tun_open(int tun, int mode)
+sys_tun_open(int tun, int mode, char **ifname)
{
struct ifreq ifr;
int fd = -1;
const char *name = NULL;
+ if (ifname != NULL)
+ *ifname = NULL;
+
if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
debug("%s: failed to open tunnel control interface: %s",
__func__, strerror(errno));
@@ -99,6 +102,9 @@ sys_tun_open(int tun, int mode)
else
debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
+ if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)))
+ goto failed;
+
return (fd);
failed:
@@ -116,13 +122,16 @@ sys_tun_open(int tun, int mode)
#endif
int
-sys_tun_open(int tun, int mode)
+sys_tun_open(int tun, int mode, char **ifname)
{
struct ifreq ifr;
char name[100];
int fd = -1, sock, flag;
const char *tunbase = "tun";
+ if (ifname != NULL)
+ *ifname = NULL;
+
if (mode == SSH_TUNMODE_ETHERNET) {
#ifdef SSH_TUN_NO_L2
debug("%s: no layer 2 tunnelling support", __func__);
@@ -180,6 +189,9 @@ sys_tun_open(int tun, int mode)
goto failed;
}
+ if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)))
+ goto failed;
+
close(sock);
return (fd);
diff --git a/openbsd-compat/port-tun.h b/openbsd-compat/port-tun.h
index 10351437..926bc93e 100644
--- a/openbsd-compat/port-tun.h
+++ b/openbsd-compat/port-tun.h
@@ -22,7 +22,7 @@ struct ssh;
#if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD)
# define CUSTOM_SYS_TUN_OPEN
-int sys_tun_open(int, int);
+int sys_tun_open(int, int, char **);
#endif
#if defined(SSH_TUN_COMPAT_AF) || defined(SSH_TUN_PREPEND_AF)
diff --git a/serverloop.c b/serverloop.c
index 24bbae32..a3cb8e78 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.198 2017/09/12 06:35:32 djm Exp $ */
+/* $OpenBSD: serverloop.c,v 1.199 2017/10/23 05:08:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -99,6 +99,9 @@ static volatile sig_atomic_t received_sigterm = 0;
/* prototypes */
static void server_init_dispatch(void);
+/* requested tunnel forwarding interface(s), shared with session.c */
+char *tun_fwd_ifnames = NULL;
+
/*
* we write to this pipe if a SIGCHLD is caught in order to avoid
* the race between select() and child_terminated
@@ -519,6 +522,7 @@ server_request_tun(struct ssh *ssh)
Channel *c = NULL;
int mode, tun;
int sock;
+ char *tmp, *ifname = NULL;
mode = packet_get_int();
switch (mode) {
@@ -541,9 +545,10 @@ server_request_tun(struct ssh *ssh)
goto done;
tun = forced_tun_device;
}
- sock = tun_open(tun, mode);
+ sock = tun_open(tun, mode, &ifname);
if (sock < 0)
goto done;
+ debug("Tunnel forwarding using interface %s", ifname);
c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
c->datagram = 1;
@@ -553,6 +558,19 @@ server_request_tun(struct ssh *ssh)
sys_tun_outfilter, NULL, NULL);
#endif
+ /*
+ * Update the list of names exposed to the session
+ * XXX remove these if the tunnels are closed (won't matter
+ * much if they are already in the environment though)
+ */
+ tmp = tun_fwd_ifnames;
+ xasprintf(&tun_fwd_ifnames, "%s%s%s",
+ tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames,
+ tun_fwd_ifnames == NULL ? "" : ",",
+ ifname);
+ free(tmp);
+ free(ifname);
+
done:
if (c == NULL)
packet_send_debug("Failed to open the tunnel device.");
diff --git a/session.c b/session.c
index 4bccb62d..a701fa0c 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.292 2017/09/12 06:32:07 djm Exp $ */
+/* $OpenBSD: session.c,v 1.293 2017/10/23 05:08:00 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -140,6 +140,7 @@ extern u_int utmp_len;
extern int startup_pipe;
extern void destroy_sensitive_data(void);
extern Buffer loginmsg;
+char *tun_fwd_ifnames; /* serverloop.c */
/* original command from peer. */
const char *original_command = NULL;
@@ -1066,6 +1067,8 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell)
free(laddr);
child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
+ if (tun_fwd_ifnames != NULL)
+ child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
if (auth_info_file != NULL)
child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
if (s->ttyfd != -1)
diff --git a/ssh.1 b/ssh.1
index 310f34cc..093f1770 100644
--- a/ssh.1
+++ b/ssh.1
@@ -33,8 +33,8 @@
.\" (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.386 2017/10/21 23:06:24 millert Exp $
-.Dd $Mdocdate: October 21 2017 $
+.\" $OpenBSD: ssh.1,v 1.387 2017/10/23 05:08:00 djm Exp $
+.Dd $Mdocdate: October 23 2017 $
.Dt SSH 1
.Os
.Sh NAME
@@ -1395,6 +1395,11 @@ This is set to the name of the tty (path to the device) associated
with the current shell or command.
If the current session has no tty,
this variable is not set.
+.It Ev SSH_TUNNEL
+Optionally set by
+.Xr sshd 8
+to contain the interface names assigned if tunnel forwarding was
+requested by the client.
.It Ev SSH_USER_AUTH
Optionally set by
.Xr sshd 8 ,
diff --git a/ssh.c b/ssh.c
index 213c35e7..74056985 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.465 2017/10/21 23:06:24 millert Exp $ */
+/* $OpenBSD: ssh.c,v 1.466 2017/10/23 05:08:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -168,6 +168,10 @@ char *config = NULL;
*/
char *host;
+/* Various strings used to to percent_expand() arguments */
+static char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
+static char uidstr[32], *host_arg, *conn_hash_hex;
+
/* socket address the host resolves to */
struct sockaddr_storage hostaddr;
@@ -208,8 +212,8 @@ usage(void)
exit(255);
}
-static int ssh_session2(struct ssh *);
-static void load_public_identity_files(void);
+static int ssh_session2(struct ssh *, struct passwd *);
+static void load_public_identity_files(struct passwd *);
static void main_sigchld_handler(int);
/* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */
@@ -456,14 +460,14 @@ resolve_canonicalize(char **hostp, int port)
* file if the user specifies a config file on the command line.
*/
static void
-process_config_files(const char *host_arg, struct passwd *pw, int post_canon)
+process_config_files(const char *host_name, struct passwd *pw, int post_canon)
{
char buf[PATH_MAX];
int r;
if (config != NULL) {
if (strcasecmp(config, "none") != 0 &&
- !read_config_file(config, pw, host, host_arg, &options,
+ !read_config_file(config, pw, host, host_name, &options,
SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0)))
fatal("Can't open user config file %.100s: "
"%.100s", config, strerror(errno));
@@ -471,13 +475,13 @@ process_config_files(const char *host_arg, struct passwd *pw, int post_canon)
r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
_PATH_SSH_USER_CONFFILE);
if (r > 0 && (size_t)r < sizeof(buf))
- (void)read_config_file(buf, pw, host, host_arg,
+ (void)read_config_file(buf, pw, host, host_name,
&options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
(post_canon ? SSHCONF_POSTCANON : 0));
/* Read systemwide configuration file after user config. */
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw,
- host, host_arg, &options,
+ host, host_name, &options,
post_canon ? SSHCONF_POSTCANON : 0);
}
}
@@ -511,9 +515,8 @@ main(int ac, char **av)
struct ssh *ssh = NULL;
int i, r, opt, exit_status, use_syslog, direct, timeout_ms;
int config_test = 0, opt_terminated = 0;
- char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile;
- char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
- char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex;
+ char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile;
+ char cname[NI_MAXHOST];
struct stat st;
struct passwd *pw;
extern int optind, optreset;
@@ -1203,6 +1206,7 @@ main(int ac, char **av)
if (options.user == NULL)
options.user = xstrdup(pw->pw_name);
+ /* Set up strings used to percent_expand() arguments */
if (gethostname(thishost, sizeof(thishost)) == -1)
fatal("gethostname: %s", strerror(errno));
strlcpy(shorthost, thishost, sizeof(shorthost));
@@ -1220,24 +1224,11 @@ main(int ac, char **av)
ssh_digest_free(md);
conn_hash_hex = tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
- if (options.local_command != NULL) {
- debug3("expanding LocalCommand: %s", options.local_command);
- cp = options.local_command;
- options.local_command = percent_expand(cp,
- "C", conn_hash_hex,
- "L", shorthost,
- "d", pw->pw_dir,
- "h", host,
- "l", thishost,
- "n", host_arg,
- "p", portstr,
- "r", options.user,
- "u", pw->pw_name,
- (char *)NULL);
- debug3("expanded LocalCommand: %s", options.local_command);
- free(cp);
- }
-
+ /*
+ * Expand tokens in arguments. NB. LocalCommand is expanded later,
+ * after port-forwarding is set up, so it may pick up any local
+ * tunnel interface name allocated.
+ */
if (options.remote_command != NULL) {
debug3("expanding RemoteCommand: %s", options.remote_command);
cp = options.remote_command;
@@ -1256,7 +1247,6 @@ main(int ac, char **av)
free(cp);
buffer_append(&command, options.remote_command,
strlen(options.remote_command));
-
}
if (options.control_path != NULL) {
@@ -1427,7 +1417,7 @@ main(int ac, char **av)
}
}
/* load options.identity_files */
- load_public_identity_files();
+ load_public_identity_files(pw);
/* optionally set the SSH_AUTHSOCKET_ENV_NAME varibale */
if (options.identity_agent &&
@@ -1491,7 +1481,7 @@ main(int ac, char **av)
}
skip_connect:
- exit_status = ssh_session2(ssh);
+ exit_status = ssh_session2(ssh, pw);
packet_close();
if (options.control_path != NULL && muxserver_sock != -1)
@@ -1650,7 +1640,7 @@ ssh_init_stdio_forwarding(struct ssh *ssh)
}
static void
-ssh_init_forwarding(struct ssh *ssh)
+ssh_init_forwarding(struct ssh *ssh, char **ifname)
{
int success = 0;
int i;
@@ -1708,8 +1698,9 @@ ssh_init_forwarding(struct ssh *ssh)
/* Initiate tunnel forwarding. */
if (options.tun_open != SSH_TUNMODE_NO) {
- if (client_request_tun_fwd(ssh, options.tun_open,
- options.tun_local, options.tun_remote) == -1) {
+ if ((*ifname = client_request_tun_fwd(ssh,
+ options.tun_open, options.tun_local,
+ options.tun_remote)) == NULL) {
if (options.exit_on_forward_failure)
fatal("Could not request tunnel forwarding.");
else
@@ -1824,14 +1815,35 @@ ssh_session2_open(struct ssh *ssh)
}
static int
-ssh_session2(struct ssh *ssh)
+ssh_session2(struct ssh *ssh, struct passwd *pw)
{
int id = -1;
+ char *cp, *tun_fwd_ifname = NULL;
/* XXX should be pre-session */
if (!options.control_persist)
ssh_init_stdio_forwarding(ssh);
- ssh_init_forwarding(ssh);
+
+ ssh_init_forwarding(ssh, &tun_fwd_ifname);
+
+ if (options.local_command != NULL) {
+ debug3("expanding LocalCommand: %s", options.local_command);
+ cp = options.local_command;
+ options.local_command = percent_expand(cp,
+ "C", conn_hash_hex,
+ "L", shorthost,
+ "d", pw->pw_dir,
+ "h", host,
+ "l", thishost,
+ "n", host_arg,
+ "p", portstr,
+ "r", options.user,
+ "u", pw->pw_name,
+ "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname,
+ (char *)NULL);
+ debug3("expanded LocalCommand: %s", options.local_command);
+ free(cp);
+ }
/* Start listening for multiplex clients */
if (!packet_get_mux())
@@ -1907,12 +1919,10 @@ ssh_session2(struct ssh *ssh)
/* Loads all IdentityFile and CertificateFile keys */
static void
-load_public_identity_files(void)
+load_public_identity_files(struct passwd *pw)
{
- char *filename, *cp, thishost[NI_MAXHOST];
- char *pwdir = NULL, *pwname = NULL;
+ char *filename, *cp;
struct sshkey *public;
- struct passwd *pw;
int i;
u_int n_ids, n_certs;
char *identity_files[SSH_MAX_IDENTITY_FILES];
@@ -1951,11 +1961,6 @@ load_public_identity_files(void)
#endif /* ENABLE_PKCS11 */
if ((pw = getpwuid(original_real_uid)) == NULL)
fatal("load_public_identity_files: getpwuid failed");
- pwname = xstrdup(pw->pw_name);
- pwdir = xstrdup(pw->pw_dir);
- if (gethostname(thishost, sizeof(thishost)) == -1)
- fatal("load_public_identity_files: gethostname: %s",
- strerror(errno));
for (i = 0; i < options.num_identity_files; i++) {
if (n_ids >= SSH_MAX_IDENTITY_FILES ||
strcasecmp(options.identity_files[i], "none") == 0) {
@@ -1965,8 +1970,8 @@ load_public_identity_files(void)
}
cp = tilde_expand_filename(options.identity_files[i],
original_real_uid);
- filename = percent_expand(cp, "d", pwdir,
- "u", pwname, "l", thishost, "h", host,
+ filename = percent_expand(cp, "d", pw->pw_dir,
+ "u", pw->pw_name, "l", thishost, "h", host,
"r", options.user, (char *)NULL);
free(cp);
public = key_load_public(filename, NULL);
@@ -2011,8 +2016,8 @@ load_public_identity_files(void)
for (i = 0; i < options.num_certificate_files; i++) {
cp = tilde_expand_filename(options.certificate_files[i],
original_real_uid);
- filename = percent_expand(cp, "d", pwdir,
- "u", pwname, "l", thishost, "h", host,
+ filename = percent_expand(cp, "d", pw->pw_dir,
+ "u", pw->pw_name, "l", thishost, "h", host,
"r", options.user, (char *)NULL);
free(cp);
@@ -2045,11 +2050,6 @@ load_public_identity_files(void)
memcpy(options.certificate_files,
certificate_files, sizeof(certificate_files));
memcpy(options.certificates, certificates, sizeof(certificates));
-
- explicit_bzero(pwname, strlen(pwname));
- free(pwname);
- explicit_bzero(pwdir, strlen(pwdir));
- free(pwdir);
}
static void
diff --git a/ssh_config.5 b/ssh_config.5
index c0470104..4d3fc342 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
.\" (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.260 2017/10/21 23:06:24 millert Exp $
-.Dd $Mdocdate: October 21 2017 $
+.\" $OpenBSD: ssh_config.5,v 1.261 2017/10/23 05:08:00 djm Exp $
+.Dd $Mdocdate: October 23 2017 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -1713,6 +1713,16 @@ The original remote hostname, as given on the command line.
The remote port.
.It %r
The remote username.
+.It \&%T
+The local
+.Xr tun 4
+or
+.Xr tap 4
+network interface assigned if
+.Cm Tunnel
+forwarding was requested, or
+.Cm NONE
+otherwise.
.It %u
The local username.
.El
@@ -1735,7 +1745,7 @@ and
accept the tokens %%, %d, %h, %l, %r, and %u.
.Pp
.Cm LocalCommand
-accepts the tokens %%, %C, %d, %h, %l, %n, %p, %r, and %u.
+accepts the tokens %%, %C, %d, %h, %l, %n, %p, %r, %T, and %u.
.Pp
.Cm ProxyCommand
accepts the tokens %%, %h, %p, and %r.