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
This commit is contained in:
djm@openbsd.org 2017-10-23 05:08:00 +00:00 committed by Damien Miller
parent 887669ef03
commit b7548b12a6
11 changed files with 133 additions and 77 deletions

View File

@ -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 */

View File

@ -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 */

12
misc.c
View File

@ -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;

4
misc.h
View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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.");

View File

@ -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)

9
ssh.1
View File

@ -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 ,

108
ssh.c
View File

@ -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

View File

@ -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.