-/* $OpenBSD: readconf.c,v 1.281 2017/12/05 23:59:47 dtucker Exp $ */
+/* $OpenBSD: readconf.c,v 1.282 2018/02/23 02:34:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
oPubkeyAuthentication,
oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
- oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
+ oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
oClearAllForwardings, oNoHostAuthenticationForLocalhost,
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
oAddressFamily, oGssAuthentication, oGssDelegateCreds,
{ "preferredauthentications", oPreferredAuthentications },
{ "hostkeyalgorithms", oHostKeyAlgorithms },
{ "bindaddress", oBindAddress },
+ { "bindinterface", oBindInterface },
{ "clearallforwardings", oClearAllForwardings },
{ "enablesshkeysign", oEnableSSHKeysign },
{ "verifyhostkeydns", oVerifyHostKeyDNS },
charptr = &options->bind_address;
goto parse_string;
+ case oBindInterface:
+ charptr = &options->bind_interface;
+ goto parse_string;
+
case oPKCS11Provider:
charptr = &options->pkcs11_provider;
goto parse_string;
options->log_level = SYSLOG_LEVEL_NOT_SET;
options->preferred_authentications = NULL;
options->bind_address = NULL;
+ options->bind_interface = NULL;
options->pkcs11_provider = NULL;
options->enable_ssh_keysign = - 1;
options->no_host_authentication_for_localhost = - 1;
/* String options */
dump_cfg_string(oBindAddress, o->bind_address);
+ dump_cfg_string(oBindInterface, o->bind_interface);
dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT);
dump_cfg_string(oControlPath, o->control_path);
dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
-/* $OpenBSD: ssh.c,v 1.473 2018/02/13 03:36:56 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.474 2018/02/23 02:34:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
usage(void)
{
fprintf(stderr,
-"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
-" [-D [bind_address:]port] [-E log_file] [-e escape_char]\n"
-" [-F configfile] [-I pkcs11] [-i identity_file]\n"
-" [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
-" [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
-" [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
-" destination [command]\n"
+"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]\n"
+" [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]\n"
+" [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]\n"
+" [-i identity_file] [-J [user@]host[:port]] [-L address]\n"
+" [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
+" [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n"
+" [-w local_tun[:remote_tun]] destination [command]\n"
);
exit(255);
}
again:
while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
- "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
+ "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
switch (opt) {
case '1':
fatal("SSH protocol v.1 is no longer supported");
case 'b':
options.bind_address = optarg;
break;
+ case 'B':
+ options.bind_interface = optarg;
+ break;
case 'F':
config = optarg;
break;
-/* $OpenBSD: sshconnect.c,v 1.294 2018/02/10 09:25:35 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.295 2018/02/23 02:34:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include <sys/socket.h>
#include <sys/time.h>
+#include <net/if.h>
#include <netinet/in.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <ifaddrs.h>
#include "xmalloc.h"
#include "ssh.h"
kill(proxy_command_pid, SIGHUP);
}
+/*
+ * Search a interface address list (returned from getifaddrs(3)) for an
+ * address that matches the desired address family on the specifed interface.
+ * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
+ */
+static int
+check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
+ struct sockaddr_storage *resultp, socklen_t *rlenp)
+{
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in *sa;
+ struct in6_addr *v6addr;
+ const struct ifaddrs *ifa;
+ int allow_local;
+
+ /*
+ * Prefer addresses that are not loopback or linklocal, but use them
+ * if nothing else matches.
+ */
+ for (allow_local = 0; allow_local < 2; allow_local++) {
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
+ (ifa->ifa_flags & IFF_UP) == 0 ||
+ ifa->ifa_addr->sa_family != af ||
+ strcmp(ifa->ifa_name, options.bind_interface) != 0)
+ continue;
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ sa = (struct sockaddr_in *)ifa->ifa_addr;
+ if (!allow_local && sa->sin_addr.s_addr ==
+ htonl(INADDR_LOOPBACK))
+ continue;
+ if (*rlenp < sizeof(struct sockaddr_in)) {
+ error("%s: v4 addr doesn't fit",
+ __func__);
+ return -1;
+ }
+ *rlenp = sizeof(struct sockaddr_in);
+ memcpy(resultp, sa, *rlenp);
+ return 0;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ v6addr = &sa6->sin6_addr;
+ if (!allow_local &&
+ (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
+ IN6_IS_ADDR_LOOPBACK(v6addr)))
+ continue;
+ if (*rlenp < sizeof(struct sockaddr_in6)) {
+ error("%s: v6 addr doesn't fit",
+ __func__);
+ return -1;
+ }
+ *rlenp = sizeof(struct sockaddr_in6);
+ memcpy(resultp, sa6, *rlenp);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
/*
* Creates a (possibly privileged) socket for use as the ssh connection.
*/
static int
ssh_create_socket(int privileged, struct addrinfo *ai)
{
- int sock, r, gaierr;
+ int sock, r, oerrno;
+ struct sockaddr_storage bindaddr;
+ socklen_t bindaddrlen = 0;
struct addrinfo hints, *res = NULL;
+ struct ifaddrs *ifaddrs = NULL;
+ char ntop[NI_MAXHOST];
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
fcntl(sock, F_SETFD, FD_CLOEXEC);
/* Bind the socket to an alternative local IP address */
- if (options.bind_address == NULL && !privileged)
+ if (options.bind_address == NULL && options.bind_interface == NULL &&
+ !privileged)
return sock;
- if (options.bind_address) {
+ if (options.bind_address != NULL) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = ai->ai_family;
hints.ai_socktype = ai->ai_socktype;
hints.ai_protocol = ai->ai_protocol;
hints.ai_flags = AI_PASSIVE;
- gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
- if (gaierr) {
+ if ((r = getaddrinfo(options.bind_address, NULL,
+ &hints, &res)) != 0) {
error("getaddrinfo: %s: %s", options.bind_address,
- ssh_gai_strerror(gaierr));
- close(sock);
- return -1;
+ ssh_gai_strerror(r));
+ goto fail;
+ }
+ if (res == NULL)
+ error("getaddrinfo: no addrs");
+ goto fail;
+ if (res->ai_addrlen > sizeof(bindaddr)) {
+ error("%s: addr doesn't fit", __func__);
+ goto fail;
+ }
+ memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
+ bindaddrlen = res->ai_addrlen;
+ } else if (options.bind_interface != NULL) {
+ if ((r = getifaddrs(&ifaddrs)) != 0) {
+ error("getifaddrs: %s: %s", options.bind_interface,
+ strerror(errno));
+ goto fail;
+ }
+ bindaddrlen = sizeof(bindaddr);
+ if (check_ifaddrs(options.bind_interface, ai->ai_family,
+ ifaddrs, &bindaddr, &bindaddrlen) != 0) {
+ logit("getifaddrs: %s: no suitable addresses",
+ options.bind_interface);
+ goto fail;
}
}
+ if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
+ ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
+ error("%s: getnameinfo failed: %s", __func__,
+ ssh_gai_strerror(r));
+ goto fail;
+ }
/*
* If we are running as root and want to connect to a privileged
* port, bind our own socket to a privileged port.
*/
if (privileged) {
PRIV_START;
- r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
+ r = bindresvport_sa(sock,
+ bindaddrlen == 0 ? NULL : (struct sockaddr *)&bindaddr);
+ oerrno = errno;
PRIV_END;
if (r < 0) {
- error("bindresvport_sa: af=%d %s", ai->ai_family,
- strerror(errno));
+ error("bindresvport_sa %s: %s", ntop,
+ strerror(oerrno));
goto fail;
}
- } else {
- if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
- error("bind: %s: %s", options.bind_address,
- strerror(errno));
- fail:
- close(sock);
- freeaddrinfo(res);
- return -1;
- }
+ } else if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
+ error("bind %s: %s", ntop, strerror(errno));
+ goto fail;
}
+ debug("%s: bound to %s", __func__, ntop);
+ /* success */
+ goto out;
+fail:
+ close(sock);
+ sock = -1;
+ out:
if (res != NULL)
freeaddrinfo(res);
+ if (ifaddrs != NULL)
+ freeifaddrs(ifaddrs);
return sock;
}