Bugzilla – Attachment 3121 Details for
Bug 2820
Add support for ssh client to bind to an interface
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
revised diff
bz2820.diff (text/plain), 10.98 KB, created by
Damien Miller
on 2018-02-16 15:37:03 AEDT
(
hide
)
Description:
revised diff
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2018-02-16 15:37:03 AEDT
Size:
10.98 KB
patch
obsolete
>commit fb09ec046dc1f57b68b8a1c471b3d89e11f62dbc >Author: Damien Miller <djm@mindrot.org> >Date: Fri Feb 16 15:39:33 2018 +1100 > > bz2820 > >diff --git a/readconf.c b/readconf.c >index 59f0032..3663a5e 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -141,7 +141,7 @@ typedef enum { > oPubkeyAuthentication, > oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, > oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, >- oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, >+ oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider, > oClearAllForwardings, oNoHostAuthenticationForLocalhost, > oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, > oAddressFamily, oGssAuthentication, oGssDelegateCreds, >@@ -251,6 +251,7 @@ static struct { > { "preferredauthentications", oPreferredAuthentications }, > { "hostkeyalgorithms", oHostKeyAlgorithms }, > { "bindaddress", oBindAddress }, >+ { "bindinterface", oBindInterface }, > { "clearallforwardings", oClearAllForwardings }, > { "enablesshkeysign", oEnableSSHKeysign }, > { "verifyhostkeydns", oVerifyHostKeyDNS }, >@@ -1084,6 +1085,10 @@ parse_char_array: > 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; >@@ -1785,6 +1790,7 @@ initialize_options(Options * options) > 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; >@@ -2492,6 +2498,7 @@ dump_client_config(Options *o, const char *host) > > /* 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); >diff --git a/readconf.h b/readconf.h >index 34aad83..da1e6d0 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -81,6 +81,7 @@ typedef struct { > char *user_hostfiles[SSH_MAX_HOSTS_FILES]; > char *preferred_authentications; > char *bind_address; /* local socket address for connection to sshd */ >+ char *bind_interface; /* local interface for bind address */ > char *pkcs11_provider; /* PKCS#11 provider */ > int verify_host_key_dns; /* Verify host key using DNS */ > >diff --git a/ssh.1 b/ssh.1 >index 9de2a11..dd7cb9c 100644 >--- a/ssh.1 >+++ b/ssh.1 >@@ -43,6 +43,7 @@ > .Sh SYNOPSIS > .Nm ssh > .Op Fl 46AaCfGgKkMNnqsTtVvXxYy >+.Op Fl B Ar bind_interface > .Op Fl b Ar bind_address > .Op Fl c Ar cipher_spec > .Op Fl D Oo Ar bind_address : Oc Ns Ar port >@@ -124,6 +125,12 @@ authenticate using the identities loaded into the agent. > .It Fl a > Disables forwarding of the authentication agent connection. > .Pp >+.It Fl B Ar interface >+Bind to the address of >+.Ar interface >+before attempting to connect to the destination host. >+This is only useful on systems with more than one address. >+.Pp > .It Fl b Ar bind_address > Use > .Ar bind_address >diff --git a/ssh.c b/ssh.c >index d3845df..abf7dc9 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -185,13 +185,13 @@ static void > 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); > } >@@ -632,7 +632,7 @@ main(int ac, char **av) > > 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"); >@@ -942,6 +942,9 @@ main(int ac, char **av) > case 'b': > options.bind_address = optarg; > break; >+ case 'B': >+ options.bind_interface = optarg; >+ break; > case 'F': > config = optarg; > break; >diff --git a/ssh_config.5 b/ssh_config.5 >index a128e4f..561ab80 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -254,6 +254,13 @@ The argument must be > or > .Cm no > (the default). >+.It Cm BindInterface >+Use the address of the specified interface on the local machine as the >+source address of the connection. >+Note that this option does not work if >+.Cm UsePrivilegedPort >+is set to >+.Cm yes . > .It Cm BindAddress > Use the specified address on the local machine as the source address of > the connection. >diff --git a/sshconnect.c b/sshconnect.c >index ede82c0..2890570 100644 >--- a/sshconnect.c >+++ b/sshconnect.c >@@ -19,6 +19,7 @@ > #include <sys/socket.h> > #include <sys/time.h> > >+#include <net/if.h> > #include <netinet/in.h> > > #include <ctype.h> >@@ -33,6 +34,7 @@ > #include <stdlib.h> > #include <string.h> > #include <unistd.h> >+#include <ifaddrs.h> > > #include "xmalloc.h" > #include "ssh.h" >@@ -258,14 +260,79 @@ ssh_kill_proxy_command(void) > 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) { >@@ -275,22 +342,50 @@ ssh_create_socket(int privileged, struct addrinfo *ai) > 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 >@@ -298,25 +393,30 @@ ssh_create_socket(int privileged, struct addrinfo *ai) > */ > 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; > } >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2820
:
3114
| 3121