diff options
author | djm@openbsd.org <djm@openbsd.org> | 2018-02-23 02:34:33 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2018-02-23 13:37:49 +1100 |
commit | ac2e3026bbee1367e4cda34765d1106099be3287 (patch) | |
tree | 83d0a8e3b1edcc01b087feb6ea98d67ec8607179 | |
parent | fcdb9d777839a3fa034b3bc3067ba8c1f6886679 (diff) |
upstream: Add BindInterface ssh_config directive and -B
command-line argument to ssh(1) that directs it to bind its outgoing
connection to the address of the specified network interface.
BindInterface prefers to use addresses that aren't loopback or link-
local, but will fall back to those if no other addresses of the
required family are available on that interface.
Based on patch by Mike Manning in bz#2820, ok dtucker@
OpenBSD-Commit-ID: c5064d285c2851f773dd736a2c342aa384fbf713
-rw-r--r-- | readconf.c | 11 | ||||
-rw-r--r-- | readconf.h | 3 | ||||
-rw-r--r-- | ssh.1 | 11 | ||||
-rw-r--r-- | ssh.c | 21 | ||||
-rw-r--r-- | ssh_config.5 | 11 | ||||
-rw-r--r-- | sshconnect.c | 142 |
6 files changed, 162 insertions, 37 deletions
diff --git a/readconf.c b/readconf.c index 10b57bd45..56bff850a 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.c,v 1.281 2017/12/05 23:59:47 dtucker Exp $ */ | 1 | /* $OpenBSD: readconf.c,v 1.282 2018/02/23 02:34:33 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -156,7 +156,7 @@ typedef enum { | |||
156 | oPubkeyAuthentication, | 156 | oPubkeyAuthentication, |
157 | oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, | 157 | oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, |
158 | oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, | 158 | oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, |
159 | oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, | 159 | oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider, |
160 | oClearAllForwardings, oNoHostAuthenticationForLocalhost, | 160 | oClearAllForwardings, oNoHostAuthenticationForLocalhost, |
161 | oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, | 161 | oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, |
162 | oAddressFamily, oGssAuthentication, oGssDelegateCreds, | 162 | oAddressFamily, oGssAuthentication, oGssDelegateCreds, |
@@ -266,6 +266,7 @@ static struct { | |||
266 | { "preferredauthentications", oPreferredAuthentications }, | 266 | { "preferredauthentications", oPreferredAuthentications }, |
267 | { "hostkeyalgorithms", oHostKeyAlgorithms }, | 267 | { "hostkeyalgorithms", oHostKeyAlgorithms }, |
268 | { "bindaddress", oBindAddress }, | 268 | { "bindaddress", oBindAddress }, |
269 | { "bindinterface", oBindInterface }, | ||
269 | { "clearallforwardings", oClearAllForwardings }, | 270 | { "clearallforwardings", oClearAllForwardings }, |
270 | { "enablesshkeysign", oEnableSSHKeysign }, | 271 | { "enablesshkeysign", oEnableSSHKeysign }, |
271 | { "verifyhostkeydns", oVerifyHostKeyDNS }, | 272 | { "verifyhostkeydns", oVerifyHostKeyDNS }, |
@@ -1099,6 +1100,10 @@ parse_char_array: | |||
1099 | charptr = &options->bind_address; | 1100 | charptr = &options->bind_address; |
1100 | goto parse_string; | 1101 | goto parse_string; |
1101 | 1102 | ||
1103 | case oBindInterface: | ||
1104 | charptr = &options->bind_interface; | ||
1105 | goto parse_string; | ||
1106 | |||
1102 | case oPKCS11Provider: | 1107 | case oPKCS11Provider: |
1103 | charptr = &options->pkcs11_provider; | 1108 | charptr = &options->pkcs11_provider; |
1104 | goto parse_string; | 1109 | goto parse_string; |
@@ -1800,6 +1805,7 @@ initialize_options(Options * options) | |||
1800 | options->log_level = SYSLOG_LEVEL_NOT_SET; | 1805 | options->log_level = SYSLOG_LEVEL_NOT_SET; |
1801 | options->preferred_authentications = NULL; | 1806 | options->preferred_authentications = NULL; |
1802 | options->bind_address = NULL; | 1807 | options->bind_address = NULL; |
1808 | options->bind_interface = NULL; | ||
1803 | options->pkcs11_provider = NULL; | 1809 | options->pkcs11_provider = NULL; |
1804 | options->enable_ssh_keysign = - 1; | 1810 | options->enable_ssh_keysign = - 1; |
1805 | options->no_host_authentication_for_localhost = - 1; | 1811 | options->no_host_authentication_for_localhost = - 1; |
@@ -2509,6 +2515,7 @@ dump_client_config(Options *o, const char *host) | |||
2509 | 2515 | ||
2510 | /* String options */ | 2516 | /* String options */ |
2511 | dump_cfg_string(oBindAddress, o->bind_address); | 2517 | dump_cfg_string(oBindAddress, o->bind_address); |
2518 | dump_cfg_string(oBindInterface, o->bind_interface); | ||
2512 | dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT); | 2519 | dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT); |
2513 | dump_cfg_string(oControlPath, o->control_path); | 2520 | dump_cfg_string(oControlPath, o->control_path); |
2514 | dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); | 2521 | dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); |
diff --git a/readconf.h b/readconf.h index 34aad83cf..f4d9e2b26 100644 --- a/readconf.h +++ b/readconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.h,v 1.124 2017/10/21 23:06:24 millert Exp $ */ | 1 | /* $OpenBSD: readconf.h,v 1.125 2018/02/23 02:34:33 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -81,6 +81,7 @@ typedef struct { | |||
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 | ||
@@ -33,8 +33,8 @@ | |||
33 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 33 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
34 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 34 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 | .\" | 35 | .\" |
36 | .\" $OpenBSD: ssh.1,v 1.389 2017/11/03 02:29:17 djm Exp $ | 36 | .\" $OpenBSD: ssh.1,v 1.390 2018/02/23 02:34:33 djm Exp $ |
37 | .Dd $Mdocdate: November 3 2017 $ | 37 | .Dd $Mdocdate: February 23 2018 $ |
38 | .Dt SSH 1 | 38 | .Dt SSH 1 |
39 | .Os | 39 | .Os |
40 | .Sh NAME | 40 | .Sh NAME |
@@ -43,6 +43,7 @@ | |||
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 |
@@ -124,6 +125,12 @@ authenticate using the identities loaded into the agent. | |||
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 |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.473 2018/02/13 03:36:56 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.474 2018/02/23 02:34:33 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -201,13 +201,13 @@ static void | |||
201 | usage(void) | 201 | usage(void) |
202 | { | 202 | { |
203 | fprintf(stderr, | 203 | fprintf(stderr, |
204 | "usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n" | 204 | "usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]\n" |
205 | " [-D [bind_address:]port] [-E log_file] [-e escape_char]\n" | 205 | " [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]\n" |
206 | " [-F configfile] [-I pkcs11] [-i identity_file]\n" | 206 | " [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]\n" |
207 | " [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n" | 207 | " [-i identity_file] [-J [user@]host[:port]] [-L address]\n" |
208 | " [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n" | 208 | " [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n" |
209 | " [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" | 209 | " [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n" |
210 | " destination [command]\n" | 210 | " [-w local_tun[:remote_tun]] destination [command]\n" |
211 | ); | 211 | ); |
212 | exit(255); | 212 | exit(255); |
213 | } | 213 | } |
@@ -663,7 +663,7 @@ main(int ac, char **av) | |||
663 | 663 | ||
664 | again: | 664 | again: |
665 | while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" | 665 | while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" |
666 | "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { | 666 | "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { |
667 | switch (opt) { | 667 | switch (opt) { |
668 | case '1': | 668 | case '1': |
669 | fatal("SSH protocol v.1 is no longer supported"); | 669 | fatal("SSH protocol v.1 is no longer supported"); |
@@ -973,6 +973,9 @@ main(int ac, char **av) | |||
973 | case 'b': | 973 | case 'b': |
974 | options.bind_address = optarg; | 974 | options.bind_address = optarg; |
975 | break; | 975 | break; |
976 | case 'B': | ||
977 | options.bind_interface = optarg; | ||
978 | break; | ||
976 | case 'F': | 979 | case 'F': |
977 | config = optarg; | 980 | config = optarg; |
978 | break; | 981 | break; |
diff --git a/ssh_config.5 b/ssh_config.5 index a128e4f0e..bdf41371c 100644 --- a/ssh_config.5 +++ b/ssh_config.5 | |||
@@ -33,8 +33,8 @@ | |||
33 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 33 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
34 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 34 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 | .\" | 35 | .\" |
36 | .\" $OpenBSD: ssh_config.5,v 1.266 2018/02/16 02:40:45 djm Exp $ | 36 | .\" $OpenBSD: ssh_config.5,v 1.267 2018/02/23 02:34:33 djm Exp $ |
37 | .Dd $Mdocdate: February 16 2018 $ | 37 | .Dd $Mdocdate: February 23 2018 $ |
38 | .Dt SSH_CONFIG 5 | 38 | .Dt SSH_CONFIG 5 |
39 | .Os | 39 | .Os |
40 | .Sh NAME | 40 | .Sh NAME |
@@ -254,6 +254,13 @@ The argument must be | |||
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. |
diff --git a/sshconnect.c b/sshconnect.c index 6eff397d6..d9618bcf7 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.294 2018/02/10 09:25:35 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.295 2018/02/23 02:34:33 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -23,6 +23,7 @@ | |||
23 | # include <sys/time.h> | 23 | # include <sys/time.h> |
24 | #endif | 24 | #endif |
25 | 25 | ||
26 | #include <net/if.h> | ||
26 | #include <netinet/in.h> | 27 | #include <netinet/in.h> |
27 | #include <arpa/inet.h> | 28 | #include <arpa/inet.h> |
28 | 29 | ||
@@ -43,6 +44,7 @@ | |||
43 | #include <stdlib.h> | 44 | #include <stdlib.h> |
44 | #include <string.h> | 45 | #include <string.h> |
45 | #include <unistd.h> | 46 | #include <unistd.h> |
47 | #include <ifaddrs.h> | ||
46 | 48 | ||
47 | #include "xmalloc.h" | 49 | #include "xmalloc.h" |
48 | #include "key.h" | 50 | #include "key.h" |
@@ -271,13 +273,78 @@ ssh_kill_proxy_command(void) | |||
271 | } | 273 | } |
272 | 274 | ||
273 | /* | 275 | /* |
276 | * Search a interface address list (returned from getifaddrs(3)) for an | ||
277 | * address that matches the desired address family on the specifed interface. | ||
278 | * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. | ||
279 | */ | ||
280 | static int | ||
281 | check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, | ||
282 | struct sockaddr_storage *resultp, socklen_t *rlenp) | ||
283 | { | ||
284 | struct sockaddr_in6 *sa6; | ||
285 | struct sockaddr_in *sa; | ||
286 | struct in6_addr *v6addr; | ||
287 | const struct ifaddrs *ifa; | ||
288 | int allow_local; | ||
289 | |||
290 | /* | ||
291 | * Prefer addresses that are not loopback or linklocal, but use them | ||
292 | * if nothing else matches. | ||
293 | */ | ||
294 | for (allow_local = 0; allow_local < 2; allow_local++) { | ||
295 | for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { | ||
296 | if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || | ||
297 | (ifa->ifa_flags & IFF_UP) == 0 || | ||
298 | ifa->ifa_addr->sa_family != af || | ||
299 | strcmp(ifa->ifa_name, options.bind_interface) != 0) | ||
300 | continue; | ||
301 | switch (ifa->ifa_addr->sa_family) { | ||
302 | case AF_INET: | ||
303 | sa = (struct sockaddr_in *)ifa->ifa_addr; | ||
304 | if (!allow_local && sa->sin_addr.s_addr == | ||
305 | htonl(INADDR_LOOPBACK)) | ||
306 | continue; | ||
307 | if (*rlenp < sizeof(struct sockaddr_in)) { | ||
308 | error("%s: v4 addr doesn't fit", | ||
309 | __func__); | ||
310 | return -1; | ||
311 | } | ||
312 | *rlenp = sizeof(struct sockaddr_in); | ||
313 | memcpy(resultp, sa, *rlenp); | ||
314 | return 0; | ||
315 | case AF_INET6: | ||
316 | sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; | ||
317 | v6addr = &sa6->sin6_addr; | ||
318 | if (!allow_local && | ||
319 | (IN6_IS_ADDR_LINKLOCAL(v6addr) || | ||
320 | IN6_IS_ADDR_LOOPBACK(v6addr))) | ||
321 | continue; | ||
322 | if (*rlenp < sizeof(struct sockaddr_in6)) { | ||
323 | error("%s: v6 addr doesn't fit", | ||
324 | __func__); | ||
325 | return -1; | ||
326 | } | ||
327 | *rlenp = sizeof(struct sockaddr_in6); | ||
328 | memcpy(resultp, sa6, *rlenp); | ||
329 | return 0; | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | return -1; | ||
334 | } | ||
335 | |||
336 | /* | ||
274 | * Creates a (possibly privileged) socket for use as the ssh connection. | 337 | * Creates a (possibly privileged) socket for use as the ssh connection. |
275 | */ | 338 | */ |
276 | static int | 339 | static int |
277 | ssh_create_socket(int privileged, struct addrinfo *ai) | 340 | ssh_create_socket(int privileged, struct addrinfo *ai) |
278 | { | 341 | { |
279 | int sock, r, gaierr; | 342 | int sock, r, oerrno; |
343 | struct sockaddr_storage bindaddr; | ||
344 | socklen_t bindaddrlen = 0; | ||
280 | struct addrinfo hints, *res = NULL; | 345 | struct addrinfo hints, *res = NULL; |
346 | struct ifaddrs *ifaddrs = NULL; | ||
347 | char ntop[NI_MAXHOST]; | ||
281 | 348 | ||
282 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 349 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
283 | if (sock < 0) { | 350 | if (sock < 0) { |
@@ -287,48 +354,81 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
287 | fcntl(sock, F_SETFD, FD_CLOEXEC); | 354 | fcntl(sock, F_SETFD, FD_CLOEXEC); |
288 | 355 | ||
289 | /* Bind the socket to an alternative local IP address */ | 356 | /* Bind the socket to an alternative local IP address */ |
290 | if (options.bind_address == NULL && !privileged) | 357 | if (options.bind_address == NULL && options.bind_interface == NULL && |
358 | !privileged) | ||
291 | return sock; | 359 | return sock; |
292 | 360 | ||
293 | if (options.bind_address) { | 361 | if (options.bind_address != NULL) { |
294 | memset(&hints, 0, sizeof(hints)); | 362 | memset(&hints, 0, sizeof(hints)); |
295 | hints.ai_family = ai->ai_family; | 363 | hints.ai_family = ai->ai_family; |
296 | hints.ai_socktype = ai->ai_socktype; | 364 | hints.ai_socktype = ai->ai_socktype; |
297 | hints.ai_protocol = ai->ai_protocol; | 365 | hints.ai_protocol = ai->ai_protocol; |
298 | hints.ai_flags = AI_PASSIVE; | 366 | hints.ai_flags = AI_PASSIVE; |
299 | gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); | 367 | if ((r = getaddrinfo(options.bind_address, NULL, |
300 | if (gaierr) { | 368 | &hints, &res)) != 0) { |
301 | error("getaddrinfo: %s: %s", options.bind_address, | 369 | error("getaddrinfo: %s: %s", options.bind_address, |
302 | ssh_gai_strerror(gaierr)); | 370 | ssh_gai_strerror(r)); |
303 | close(sock); | 371 | goto fail; |
304 | return -1; | 372 | } |
373 | if (res == NULL) | ||
374 | error("getaddrinfo: no addrs"); | ||
375 | goto fail; | ||
376 | if (res->ai_addrlen > sizeof(bindaddr)) { | ||
377 | error("%s: addr doesn't fit", __func__); | ||
378 | goto fail; | ||
379 | } | ||
380 | memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); | ||
381 | bindaddrlen = res->ai_addrlen; | ||
382 | } else if (options.bind_interface != NULL) { | ||
383 | if ((r = getifaddrs(&ifaddrs)) != 0) { | ||
384 | error("getifaddrs: %s: %s", options.bind_interface, | ||
385 | strerror(errno)); | ||
386 | goto fail; | ||
387 | } | ||
388 | bindaddrlen = sizeof(bindaddr); | ||
389 | if (check_ifaddrs(options.bind_interface, ai->ai_family, | ||
390 | ifaddrs, &bindaddr, &bindaddrlen) != 0) { | ||
391 | logit("getifaddrs: %s: no suitable addresses", | ||
392 | options.bind_interface); | ||
393 | goto fail; | ||
305 | } | 394 | } |
306 | } | 395 | } |
396 | if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, | ||
397 | ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { | ||
398 | error("%s: getnameinfo failed: %s", __func__, | ||
399 | ssh_gai_strerror(r)); | ||
400 | goto fail; | ||
401 | } | ||
307 | /* | 402 | /* |
308 | * If we are running as root and want to connect to a privileged | 403 | * If we are running as root and want to connect to a privileged |
309 | * port, bind our own socket to a privileged port. | 404 | * port, bind our own socket to a privileged port. |
310 | */ | 405 | */ |
311 | if (privileged) { | 406 | if (privileged) { |
312 | PRIV_START; | 407 | PRIV_START; |
313 | r = bindresvport_sa(sock, res ? res->ai_addr : NULL); | 408 | r = bindresvport_sa(sock, |
409 | bindaddrlen == 0 ? NULL : (struct sockaddr *)&bindaddr); | ||
410 | oerrno = errno; | ||
314 | PRIV_END; | 411 | PRIV_END; |
315 | if (r < 0) { | 412 | if (r < 0) { |
316 | error("bindresvport_sa: af=%d %s", ai->ai_family, | 413 | error("bindresvport_sa %s: %s", ntop, |
317 | strerror(errno)); | 414 | strerror(oerrno)); |
318 | goto fail; | 415 | goto fail; |
319 | } | 416 | } |
320 | } else { | 417 | } else if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { |
321 | if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | 418 | error("bind %s: %s", ntop, strerror(errno)); |
322 | error("bind: %s: %s", options.bind_address, | 419 | goto fail; |
323 | strerror(errno)); | ||
324 | fail: | ||
325 | close(sock); | ||
326 | freeaddrinfo(res); | ||
327 | return -1; | ||
328 | } | ||
329 | } | 420 | } |
421 | debug("%s: bound to %s", __func__, ntop); | ||
422 | /* success */ | ||
423 | goto out; | ||
424 | fail: | ||
425 | close(sock); | ||
426 | sock = -1; | ||
427 | out: | ||
330 | if (res != NULL) | 428 | if (res != NULL) |
331 | freeaddrinfo(res); | 429 | freeaddrinfo(res); |
430 | if (ifaddrs != NULL) | ||
431 | freeifaddrs(ifaddrs); | ||
332 | return sock; | 432 | return sock; |
333 | } | 433 | } |
334 | 434 | ||