View | Details | Raw Unified | Return to bug 2820
Collapse All | Expand All

(-)a/readconf.c (-1 / +8 lines)
Lines 141-147 typedef enum { Link Here
141
	oPubkeyAuthentication,
141
	oPubkeyAuthentication,
142
	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
142
	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
143
	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
143
	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
144
	oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
144
	oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
145
	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
145
	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
146
	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
146
	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
147
	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
147
	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
Lines 251-256 static struct { Link Here
251
	{ "preferredauthentications", oPreferredAuthentications },
251
	{ "preferredauthentications", oPreferredAuthentications },
252
	{ "hostkeyalgorithms", oHostKeyAlgorithms },
252
	{ "hostkeyalgorithms", oHostKeyAlgorithms },
253
	{ "bindaddress", oBindAddress },
253
	{ "bindaddress", oBindAddress },
254
	{ "bindinterface", oBindInterface },
254
	{ "clearallforwardings", oClearAllForwardings },
255
	{ "clearallforwardings", oClearAllForwardings },
255
	{ "enablesshkeysign", oEnableSSHKeysign },
256
	{ "enablesshkeysign", oEnableSSHKeysign },
256
	{ "verifyhostkeydns", oVerifyHostKeyDNS },
257
	{ "verifyhostkeydns", oVerifyHostKeyDNS },
Lines 1084-1089 parse_char_array: Link Here
1084
		charptr = &options->bind_address;
1085
		charptr = &options->bind_address;
1085
		goto parse_string;
1086
		goto parse_string;
1086
1087
1088
	case oBindInterface:
1089
		charptr = &options->bind_interface;
1090
		goto parse_string;
1091
1087
	case oPKCS11Provider:
1092
	case oPKCS11Provider:
1088
		charptr = &options->pkcs11_provider;
1093
		charptr = &options->pkcs11_provider;
1089
		goto parse_string;
1094
		goto parse_string;
Lines 1785-1790 initialize_options(Options * options) Link Here
1785
	options->log_level = SYSLOG_LEVEL_NOT_SET;
1790
	options->log_level = SYSLOG_LEVEL_NOT_SET;
1786
	options->preferred_authentications = NULL;
1791
	options->preferred_authentications = NULL;
1787
	options->bind_address = NULL;
1792
	options->bind_address = NULL;
1793
	options->bind_interface = NULL;
1788
	options->pkcs11_provider = NULL;
1794
	options->pkcs11_provider = NULL;
1789
	options->enable_ssh_keysign = - 1;
1795
	options->enable_ssh_keysign = - 1;
1790
	options->no_host_authentication_for_localhost = - 1;
1796
	options->no_host_authentication_for_localhost = - 1;
Lines 2492-2497 dump_client_config(Options *o, const char *host) Link Here
2492
2498
2493
	/* String options */
2499
	/* String options */
2494
	dump_cfg_string(oBindAddress, o->bind_address);
2500
	dump_cfg_string(oBindAddress, o->bind_address);
2501
	dump_cfg_string(oBindInterface, o->bind_interface);
2495
	dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT);
2502
	dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT);
2496
	dump_cfg_string(oControlPath, o->control_path);
2503
	dump_cfg_string(oControlPath, o->control_path);
2497
	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
2504
	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
(-)a/readconf.h (+1 lines)
Lines 81-86 typedef struct { Link Here
81
	char   *user_hostfiles[SSH_MAX_HOSTS_FILES];
81
	char   *user_hostfiles[SSH_MAX_HOSTS_FILES];
82
	char   *preferred_authentications;
82
	char   *preferred_authentications;
83
	char   *bind_address;	/* local socket address for connection to sshd */
83
	char   *bind_address;	/* local socket address for connection to sshd */
84
	char   *bind_interface;	/* local interface for bind address */
84
	char   *pkcs11_provider; /* PKCS#11 provider */
85
	char   *pkcs11_provider; /* PKCS#11 provider */
85
	int	verify_host_key_dns;	/* Verify host key using DNS */
86
	int	verify_host_key_dns;	/* Verify host key using DNS */
86
87
(-)a/ssh.1 (+7 lines)
Lines 43-48 Link Here
43
.Sh SYNOPSIS
43
.Sh SYNOPSIS
44
.Nm ssh
44
.Nm ssh
45
.Op Fl 46AaCfGgKkMNnqsTtVvXxYy
45
.Op Fl 46AaCfGgKkMNnqsTtVvXxYy
46
.Op Fl B Ar bind_interface
46
.Op Fl b Ar bind_address
47
.Op Fl b Ar bind_address
47
.Op Fl c Ar cipher_spec
48
.Op Fl c Ar cipher_spec
48
.Op Fl D Oo Ar bind_address : Oc Ns Ar port
49
.Op Fl D Oo Ar bind_address : Oc Ns Ar port
Lines 124-129 authenticate using the identities loaded into the agent. Link Here
124
.It Fl a
125
.It Fl a
125
Disables forwarding of the authentication agent connection.
126
Disables forwarding of the authentication agent connection.
126
.Pp
127
.Pp
128
.It Fl B Ar interface
129
Bind to the address of
130
.Ar interface
131
before attempting to connect to the destination host.
132
This is only useful on systems with more than one address.
133
.Pp
127
.It Fl b Ar bind_address
134
.It Fl b Ar bind_address
128
Use
135
Use
129
.Ar bind_address
136
.Ar bind_address
(-)a/ssh.c (-8 / +11 lines)
Lines 185-197 static void Link Here
185
usage(void)
185
usage(void)
186
{
186
{
187
	fprintf(stderr,
187
	fprintf(stderr,
188
"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
188
"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]\n"
189
"           [-D [bind_address:]port] [-E log_file] [-e escape_char]\n"
189
"           [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]\n"
190
"           [-F configfile] [-I pkcs11] [-i identity_file]\n"
190
"           [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]\n"
191
"           [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
191
"           [-i identity_file] [-J [user@]host[:port]] [-L address]\n"
192
"           [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
192
"           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
193
"           [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
193
"           [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n"
194
"           destination [command]\n"
194
"           [-w local_tun[:remote_tun]] destination [command]\n"
195
	);
195
	);
196
	exit(255);
196
	exit(255);
197
}
197
}
Lines 632-638 main(int ac, char **av) Link Here
632
632
633
 again:
633
 again:
634
	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
634
	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
635
	    "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
635
	    "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
636
		switch (opt) {
636
		switch (opt) {
637
		case '1':
637
		case '1':
638
			fatal("SSH protocol v.1 is no longer supported");
638
			fatal("SSH protocol v.1 is no longer supported");
Lines 942-947 main(int ac, char **av) Link Here
942
		case 'b':
942
		case 'b':
943
			options.bind_address = optarg;
943
			options.bind_address = optarg;
944
			break;
944
			break;
945
		case 'B':
946
			options.bind_interface = optarg;
947
			break;
945
		case 'F':
948
		case 'F':
946
			config = optarg;
949
			config = optarg;
947
			break;
950
			break;
(-)a/ssh_config.5 (+7 lines)
Lines 254-259 The argument must be Link Here
254
or
254
or
255
.Cm no
255
.Cm no
256
(the default).
256
(the default).
257
.It Cm BindInterface
258
Use the address of the specified interface on the local machine as the
259
source address of the connection.
260
Note that this option does not work if
261
.Cm UsePrivilegedPort
262
is set to
263
.Cm yes .
257
.It Cm BindAddress
264
.It Cm BindAddress
258
Use the specified address on the local machine as the source address of
265
Use the specified address on the local machine as the source address of
259
the connection.
266
the connection.
(-)a/sshconnect.c (-20 / +120 lines)
Lines 19-24 Link Here
19
#include <sys/socket.h>
19
#include <sys/socket.h>
20
#include <sys/time.h>
20
#include <sys/time.h>
21
21
22
#include <net/if.h>
22
#include <netinet/in.h>
23
#include <netinet/in.h>
23
24
24
#include <ctype.h>
25
#include <ctype.h>
Lines 33-38 Link Here
33
#include <stdlib.h>
34
#include <stdlib.h>
34
#include <string.h>
35
#include <string.h>
35
#include <unistd.h>
36
#include <unistd.h>
37
#include <ifaddrs.h>
36
38
37
#include "xmalloc.h"
39
#include "xmalloc.h"
38
#include "ssh.h"
40
#include "ssh.h"
Lines 258-271 ssh_kill_proxy_command(void) Link Here
258
		kill(proxy_command_pid, SIGHUP);
260
		kill(proxy_command_pid, SIGHUP);
259
}
261
}
260
262
263
/*
264
 * Search a interface address list (returned from getifaddrs(3)) for an
265
 * address that matches the desired address family on the specifed interface.
266
 * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
267
 */
268
static int
269
check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
270
    struct sockaddr_storage *resultp, socklen_t *rlenp)
271
{
272
	struct sockaddr_in6 *sa6;
273
	struct sockaddr_in *sa;
274
	struct in6_addr *v6addr;
275
	const struct ifaddrs *ifa;
276
	int allow_local;
277
278
	/*
279
	 * Prefer addresses that are not loopback or linklocal, but use them
280
	 * if nothing else matches.
281
	 */
282
	for (allow_local = 0; allow_local < 2; allow_local++) {
283
		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
284
			if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
285
			    (ifa->ifa_flags & IFF_UP) == 0 ||
286
			    ifa->ifa_addr->sa_family != af ||
287
			    strcmp(ifa->ifa_name, options.bind_interface) != 0)
288
				continue;
289
			switch (ifa->ifa_addr->sa_family) {
290
			case AF_INET:
291
				sa = (struct sockaddr_in *)ifa->ifa_addr;
292
				if (!allow_local && sa->sin_addr.s_addr ==
293
				    htonl(INADDR_LOOPBACK))
294
					continue;
295
				if (*rlenp < sizeof(struct sockaddr_in)) {
296
					error("%s: v4 addr doesn't fit",
297
					    __func__);
298
					return -1;
299
				}
300
				*rlenp = sizeof(struct sockaddr_in);
301
				memcpy(resultp, sa, *rlenp);
302
				return 0;
303
			case AF_INET6:
304
				sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
305
				v6addr = &sa6->sin6_addr;
306
				if (!allow_local &&
307
				    (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
308
				    IN6_IS_ADDR_LOOPBACK(v6addr)))
309
					continue;
310
				if (*rlenp < sizeof(struct sockaddr_in6)) {
311
					error("%s: v6 addr doesn't fit",
312
					    __func__);
313
					return -1;
314
				}
315
				*rlenp = sizeof(struct sockaddr_in6);
316
				memcpy(resultp, sa6, *rlenp);
317
				return 0;
318
			}
319
		}
320
	}
321
	return -1;
322
}
323
261
/*
324
/*
262
 * Creates a (possibly privileged) socket for use as the ssh connection.
325
 * Creates a (possibly privileged) socket for use as the ssh connection.
263
 */
326
 */
264
static int
327
static int
265
ssh_create_socket(int privileged, struct addrinfo *ai)
328
ssh_create_socket(int privileged, struct addrinfo *ai)
266
{
329
{
267
	int sock, r, gaierr;
330
	int sock, r, oerrno;
331
	struct sockaddr_storage bindaddr;
332
	socklen_t bindaddrlen = 0;
268
	struct addrinfo hints, *res = NULL;
333
	struct addrinfo hints, *res = NULL;
334
	struct ifaddrs *ifaddrs = NULL;
335
	char ntop[NI_MAXHOST];
269
336
270
	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
337
	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
271
	if (sock < 0) {
338
	if (sock < 0) {
Lines 275-296 ssh_create_socket(int privileged, struct addrinfo *ai) Link Here
275
	fcntl(sock, F_SETFD, FD_CLOEXEC);
342
	fcntl(sock, F_SETFD, FD_CLOEXEC);
276
343
277
	/* Bind the socket to an alternative local IP address */
344
	/* Bind the socket to an alternative local IP address */
278
	if (options.bind_address == NULL && !privileged)
345
	if (options.bind_address == NULL && options.bind_interface == NULL &&
346
	    !privileged)
279
		return sock;
347
		return sock;
280
348
281
	if (options.bind_address) {
349
	if (options.bind_address != NULL) {
282
		memset(&hints, 0, sizeof(hints));
350
		memset(&hints, 0, sizeof(hints));
283
		hints.ai_family = ai->ai_family;
351
		hints.ai_family = ai->ai_family;
284
		hints.ai_socktype = ai->ai_socktype;
352
		hints.ai_socktype = ai->ai_socktype;
285
		hints.ai_protocol = ai->ai_protocol;
353
		hints.ai_protocol = ai->ai_protocol;
286
		hints.ai_flags = AI_PASSIVE;
354
		hints.ai_flags = AI_PASSIVE;
287
		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
355
		if ((r = getaddrinfo(options.bind_address, NULL,
288
		if (gaierr) {
356
		    &hints, &res)) != 0) {
289
			error("getaddrinfo: %s: %s", options.bind_address,
357
			error("getaddrinfo: %s: %s", options.bind_address,
290
			    ssh_gai_strerror(gaierr));
358
			    ssh_gai_strerror(r));
291
			close(sock);
359
			goto fail;
292
			return -1;
293
		}
360
		}
361
		if (res == NULL)
362
			error("getaddrinfo: no addrs");
363
			goto fail;
364
		if (res->ai_addrlen > sizeof(bindaddr)) {
365
			error("%s: addr doesn't fit", __func__);
366
			goto fail;
367
		}
368
		memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
369
		bindaddrlen = res->ai_addrlen;
370
	} else if (options.bind_interface != NULL) {
371
		if ((r = getifaddrs(&ifaddrs)) != 0) {
372
			error("getifaddrs: %s: %s", options.bind_interface,
373
			      strerror(errno));
374
			goto fail;
375
		}
376
		bindaddrlen = sizeof(bindaddr);
377
		if (check_ifaddrs(options.bind_interface, ai->ai_family,
378
		    ifaddrs, &bindaddr, &bindaddrlen) != 0) {
379
			logit("getifaddrs: %s: no suitable addresses",
380
			      options.bind_interface);
381
			goto fail;
382
		}
383
	}
384
	if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
385
	    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
386
		error("%s: getnameinfo failed: %s", __func__,
387
		    ssh_gai_strerror(r));
388
		goto fail;
294
	}
389
	}
295
	/*
390
	/*
296
	 * If we are running as root and want to connect to a privileged
391
	 * If we are running as root and want to connect to a privileged
Lines 298-322 ssh_create_socket(int privileged, struct addrinfo *ai) Link Here
298
	 */
393
	 */
299
	if (privileged) {
394
	if (privileged) {
300
		PRIV_START;
395
		PRIV_START;
301
		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
396
		r = bindresvport_sa(sock,
397
		        bindaddrlen == 0 ? NULL : (struct sockaddr *)&bindaddr);
398
		oerrno = errno;
302
		PRIV_END;
399
		PRIV_END;
303
		if (r < 0) {
400
		if (r < 0) {
304
			error("bindresvport_sa: af=%d %s", ai->ai_family,
401
			error("bindresvport_sa %s: %s", ntop,
305
			    strerror(errno));
402
			    strerror(oerrno));
306
			goto fail;
403
			goto fail;
307
		}
404
		}
308
	} else {
405
	} else if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
309
		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
406
		error("bind %s: %s", ntop, strerror(errno));
310
			error("bind: %s: %s", options.bind_address,
407
		goto fail;
311
			    strerror(errno));
312
 fail:
313
			close(sock);
314
			freeaddrinfo(res);
315
			return -1;
316
		}
317
	}
408
	}
409
	debug("%s: bound to %s", __func__, ntop);
410
	/* success */
411
	goto out;
412
fail:
413
	close(sock);
414
	sock = -1;
415
 out:
318
	if (res != NULL)
416
	if (res != NULL)
319
		freeaddrinfo(res);
417
		freeaddrinfo(res);
418
	if (ifaddrs != NULL)
419
		freeifaddrs(ifaddrs);
320
	return sock;
420
	return sock;
321
}
421
}
322
422

Return to bug 2820