Add BIO_bind function to bind local address for a socket.
authorJohn Hughes <john@atlantech.com>
Thu, 8 Feb 2018 09:49:02 +0000 (10:49 +0100)
committerAndy Polyakov <appro@openssl.org>
Mon, 19 Feb 2018 21:58:37 +0000 (22:58 +0100)
Add -bind option to s_client application to allow specification of
local address for connection.

Reviewed-by: Andy Polyakov <appro@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5272)

apps/s_apps.h
apps/s_client.c
apps/s_socket.c
crypto/bio/b_sock2.c
crypto/bio/bio_err.c
crypto/err/openssl.txt
doc/man1/s_client.pod
doc/man3/BIO_connect.pod
include/openssl/bio.h
include/openssl/bioerr.h
util/libcrypto.num

index 6ee2b0c8cae68bcb9f30de24d594f026cfee1cbc..fbffd86e710defc2d014544ea1aded08282aad56 100644 (file)
@@ -38,6 +38,7 @@ int ssl_print_groups(BIO *out, SSL *s, int noshared);
 #endif
 int ssl_print_tmp_key(BIO *out, SSL *s);
 int init_client(int *sock, const char *host, const char *port,
+                const char *bindhost, const char *bindport,
                 int family, int type, int protocol);
 int should_retry(int i);
 
index eca0a4ac57e85e3899c199e985229c0b470f2ceb..a319d217c1f25518e07cab266d4485257e70ad6d 100644 (file)
@@ -570,7 +570,7 @@ static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset)
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX,
+    OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_BIND, OPT_UNIX,
     OPT_XMPPHOST, OPT_VERIFY, OPT_NAMEOPT,
     OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN,
     OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
@@ -612,6 +612,7 @@ const OPTIONS s_client_options[] = {
     {"port", OPT_PORT, 'p', "Use -connect instead"},
     {"connect", OPT_CONNECT, 's',
      "TCP/IP where to connect (default is :" PORT ")"},
+    {"bind", OPT_BIND, 's', "bind local address for connection"},
     {"proxy", OPT_PROXY, 's',
      "Connect to via specified proxy to the real server"},
 #ifdef AF_UNIX
@@ -884,10 +885,11 @@ int s_client_main(int argc, char **argv)
     const SSL_METHOD *meth = TLS_client_method();
     const char *CApath = NULL, *CAfile = NULL;
     char *cbuf = NULL, *sbuf = NULL;
-    char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL;
+    char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL, *bindstr = NULL;
     char *cert_file = NULL, *key_file = NULL, *chain_file = NULL;
     char *chCApath = NULL, *chCAfile = NULL, *host = NULL;
     char *port = OPENSSL_strdup(PORT);
+    char *bindhost = NULL, *bindport = NULL;
     char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL;
     char *ReqCAfile = NULL;
     char *sess_in = NULL, *crl_file = NULL, *p;
@@ -1053,6 +1055,9 @@ int s_client_main(int argc, char **argv)
             connect_type = use_inet;
             freeandcopy(&connectstr, opt_arg());
             break;
+        case OPT_BIND:
+            freeandcopy(&bindstr, opt_arg());
+            break;
         case OPT_PROXY:
             proxystr = opt_arg();
             starttls_proto = PROTO_CONNECT;
@@ -1554,6 +1559,18 @@ int s_client_main(int argc, char **argv)
         }
     }
 
+    if (bindstr != NULL) {
+        int res;
+        res = BIO_parse_hostserv(bindstr, &bindhost, &bindport,
+                                 BIO_PARSE_PRIO_HOST);
+        if (!res) {
+            BIO_printf(bio_err,
+                       "%s: -bind argument parameter malformed or ambiguous\n",
+                       prog);
+            goto end;
+        }
+    }
+
 #ifdef AF_UNIX
     if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) {
         BIO_printf(bio_err,
@@ -1976,8 +1993,8 @@ int s_client_main(int argc, char **argv)
     }
 
  re_start:
-    if (init_client(&s, host, port, socket_family, socket_type, protocol)
-            == 0) {
+    if (init_client(&s, host, port, bindhost, bindport, socket_family,
+                    socket_type, protocol) == 0) {
         BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error());
         BIO_closesocket(s);
         goto end;
@@ -3082,6 +3099,7 @@ int s_client_main(int argc, char **argv)
     OPENSSL_free(srp_arg.srppassin);
 #endif
     OPENSSL_free(connectstr);
+    OPENSSL_free(bindstr);
     OPENSSL_free(host);
     OPENSSL_free(port);
     X509_VERIFY_PARAM_free(vpm);
index 485b419387da1399adc265d701557c602eb374b8..4b82011acd1de8b48ec22994ca4a85749b927df6 100644 (file)
@@ -43,6 +43,8 @@ BIO_ADDR *ourpeer = NULL;
  * @sock: pointer to storage of resulting socket.
  * @host: the host name or path (for AF_UNIX) to connect to.
  * @port: the port to connect to (ignored for AF_UNIX).
+ * @bindhost: source host or path (for AF_UNIX).
+ * @bindport: source port (ignored for AF_UNIX).
  * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
  *  AF_UNSPEC
  * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
@@ -58,10 +60,14 @@ BIO_ADDR *ourpeer = NULL;
  * Returns 1 on success, 0 on failure.
  */
 int init_client(int *sock, const char *host, const char *port,
+                const char *bindhost, const char *bindport,
                 int family, int type, int protocol)
 {
     BIO_ADDRINFO *res = NULL;
+    BIO_ADDRINFO *bindaddr = NULL;
     const BIO_ADDRINFO *ai = NULL;
+    const BIO_ADDRINFO *bi = NULL;
+    int found = 0;
     int ret;
 
     if (BIO_sock_init() != 1)
@@ -74,6 +80,15 @@ int init_client(int *sock, const char *host, const char *port,
         return 0;
     }
 
+    if (bindhost != NULL || bindport != NULL) {
+        ret = BIO_lookup_ex(bindhost, bindport, BIO_LOOKUP_CLIENT,
+                            family, type, protocol, &bindaddr);
+        if (ret == 0) {
+            ERR_print_errors (bio_err);
+            goto out;
+        }
+    }
+
     ret = 0;
     for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
         /* Admittedly, these checks are quite paranoid, we should not get
@@ -85,6 +100,16 @@ int init_client(int *sock, const char *host, const char *port,
                        && (protocol == 0
                            || protocol == BIO_ADDRINFO_protocol(ai)));
 
+        if (bindaddr != NULL) {
+            for (bi = bindaddr; bi != NULL; bi = BIO_ADDRINFO_next(bi)) {
+                if (BIO_ADDRINFO_family(bi) == BIO_ADDRINFO_family(ai))
+                    break;
+            }
+            if (bi == NULL)
+                continue;
+            ++found;
+        }
+
         *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai),
                            BIO_ADDRINFO_protocol(ai), 0);
         if (*sock == INVALID_SOCKET) {
@@ -94,6 +119,15 @@ int init_client(int *sock, const char *host, const char *port,
             continue;
         }
 
+        if (bi != NULL) {
+            if (!BIO_bind(*sock, BIO_ADDRINFO_address(bi),
+                          BIO_SOCK_REUSEADDR)) {
+                BIO_closesocket(*sock);
+                *sock = INVALID_SOCKET;
+                break;
+            }
+        }
+
 #ifndef OPENSSL_NO_SCTP
         if (protocol == IPPROTO_SCTP) {
             /*
@@ -123,12 +157,27 @@ int init_client(int *sock, const char *host, const char *port,
     }
 
     if (*sock == INVALID_SOCKET) {
+        if (bindaddr != NULL && !found) {
+            BIO_printf(bio_err, "Can't bind %saddress for %s%s%s\n",
+                       BIO_ADDRINFO_family(res) == AF_INET6 ? "IPv6 " :
+                       BIO_ADDRINFO_family(res) == AF_INET ? "IPv4 " :
+                       BIO_ADDRINFO_family(res) == AF_UNIX ? "unix " : "",
+                       bindhost != NULL ? bindhost : "",
+                       bindport != NULL ? ":" : "",
+                       bindport != NULL ? bindport : "");
+            ERR_clear_error();
+            ret = 0;
+        }
         ERR_print_errors(bio_err);
     } else {
         /* Remove any stale errors from previous connection attempts */
         ERR_clear_error();
         ret = 1;
     }
+out:
+    if (bindaddr != NULL) {
+        BIO_ADDRINFO_free (bindaddr);
+    }
     BIO_ADDRINFO_free(res);
     return ret;
 }
index 12a3b3624389a80dddb306b8f86fd4b8c6d07920..823732d64e1a352880d01e22735af6cfdc48785d 100644 (file)
@@ -115,6 +115,55 @@ int BIO_connect(int sock, const BIO_ADDR *addr, int options)
     return 1;
 }
 
+/*-
+ * BIO_bind - bind socket to address
+ * @sock: the socket to set
+ * @addr: local address to bind to
+ * @options: BIO socket options
+ *
+ * Binds to the address using the given socket and options.
+ *
+ * Options can be a combination of the following:
+ * - BIO_SOCK_REUSEADDR: Try to reuse the address and port combination
+ *   for a recently closed port.
+ *
+ * When restarting the program it could be that the port is still in use.  If
+ * you set to BIO_SOCK_REUSEADDR option it will try to reuse the port anyway.
+ * It's recommended that you use this.
+ */
+int BIO_bind(int sock, const BIO_ADDR *addr, int options)
+{
+    int on = 1;
+
+    if (sock == -1) {
+        BIOerr(BIO_F_BIO_BIND, BIO_R_INVALID_SOCKET);
+        return 0;
+    }
+
+# ifndef OPENSSL_SYS_WINDOWS
+    /*
+     * SO_REUSEADDR has different behavior on Windows than on
+     * other operating systems, don't set it there.
+     */
+    if (options & BIO_SOCK_REUSEADDR) {
+        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+                       (const void *)&on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_BIND, BIO_R_UNABLE_TO_REUSEADDR);
+            return 0;
+        }
+    }
+# endif
+
+    if (bind(sock, BIO_ADDR_sockaddr(addr), BIO_ADDR_sockaddr_size(addr)) != 0) {
+        SYSerr(SYS_F_BIND, get_last_socket_error());
+        BIOerr(BIO_F_BIO_BIND, BIO_R_UNABLE_TO_BIND_SOCKET);
+        return 0;
+    }
+
+    return 1;
+}
+
 /*-
  * BIO_listen - Creates a listen socket
  * @sock: the socket to listen with
@@ -174,21 +223,6 @@ int BIO_listen(int sock, const BIO_ADDR *addr, int options)
     if (!BIO_socket_nbio(sock, (options & BIO_SOCK_NONBLOCK) != 0))
         return 0;
 
-# ifndef OPENSSL_SYS_WINDOWS
-    /*
-     * SO_REUSEADDR has different behavior on Windows than on
-     * other operating systems, don't set it there.
-     */
-    if (options & BIO_SOCK_REUSEADDR) {
-        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-                       (const void *)&on, sizeof(on)) != 0) {
-            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
-            BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_REUSEADDR);
-            return 0;
-        }
-    }
-# endif
-
     if (options & BIO_SOCK_KEEPALIVE) {
         if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
                        (const void *)&on, sizeof(on)) != 0) {
@@ -223,11 +257,8 @@ int BIO_listen(int sock, const BIO_ADDR *addr, int options)
     }
 # endif
 
-    if (bind(sock, BIO_ADDR_sockaddr(addr), BIO_ADDR_sockaddr_size(addr)) != 0) {
-        SYSerr(SYS_F_BIND, get_last_socket_error());
-        BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_BIND_SOCKET);
+    if (!BIO_bind(sock, addr, options))
         return 0;
-    }
 
     if (socktype != SOCK_DGRAM && listen(sock, MAX_LISTEN) == -1) {
         SYSerr(SYS_F_LISTEN, get_last_socket_error());
index 20f3aa14f8caaaf57bc7d5114b6893adf43b698f..594013dd9859b4a79f6109cd5d14b56a32fa5d0a 100644 (file)
@@ -19,6 +19,7 @@ static const ERR_STRING_DATA BIO_str_functs[] = {
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ACCEPT, 0), "BIO_accept"},
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ACCEPT_EX, 0), "BIO_accept_ex"},
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ADDR_NEW, 0), "BIO_ADDR_new"},
+    {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_BIND, 0), "BIO_bind"},
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CALLBACK_CTRL, 0), "BIO_callback_ctrl"},
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CONNECT, 0), "BIO_connect"},
     {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CTRL, 0), "BIO_ctrl"},
index 43001be9d93f298eb20db14727a0c5725b37b6f1..9b8a03ac21e5e30b3b47827d84abdd218e3cedd2 100644 (file)
@@ -116,6 +116,7 @@ BIO_F_ADDR_STRINGS:134:addr_strings
 BIO_F_BIO_ACCEPT:101:BIO_accept
 BIO_F_BIO_ACCEPT_EX:137:BIO_accept_ex
 BIO_F_BIO_ADDR_NEW:144:BIO_ADDR_new
+BIO_F_BIO_BIND:147:BIO_bind
 BIO_F_BIO_CALLBACK_CTRL:131:BIO_callback_ctrl
 BIO_F_BIO_CONNECT:138:BIO_connect
 BIO_F_BIO_CTRL:103:BIO_ctrl
index 8d8eac76f4a46e5d63db8000b4c058695c193802..6e47140f41c71673566e461d0de3b0fd4c36f57f 100644 (file)
@@ -10,6 +10,7 @@ s_client - SSL/TLS client program
 B<openssl> B<s_client>
 [B<-help>]
 [B<-connect host:port>]
+[B<-bind host:port>]
 [B<-proxy host:port>]
 [B<-unix path>]
 [B<-4>]
@@ -147,6 +148,12 @@ select the host and port using the optional target positional argument instead.
 If neither this nor the target positonal argument are specified then an attempt
 is made to connect to the local host on port 4433.
 
+=item B<-bind host:port>]
+
+This specifies the host address and or port to bind as the source for the
+connection.  For Unix-domain sockets the port is ignored and the host is
+used as the source socket address.
+
 =item B<-proxy host:port>
 
 When used with the B<-connect> flag, the program uses the host and port
@@ -686,6 +693,9 @@ applications should B<not> do this as it makes them vulnerable to a MITM
 attack. This behaviour can be changed by with the B<-verify_return_error>
 option: any verify errors are then returned aborting the handshake.
 
+The B<-bind> option may be useful if the server or a firewall requires
+connections to come from some particular address and or port.
+
 =head1 BUGS
 
 Because this program has a lot of options and also because some of the
index 91dcab1178b2047be37563258fc3c47948332a9a..454832e7e032f87870eada4c39d80db026aba1db 100644 (file)
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-BIO_socket, BIO_connect, BIO_listen, BIO_accept_ex, BIO_closesocket - BIO
+BIO_socket, BIO_bind, BIO_connect, BIO_listen, BIO_accept_ex, BIO_closesocket - BIO
 socket communication setup routines
 
 =head1 SYNOPSIS
@@ -10,6 +10,7 @@ socket communication setup routines
  #include <openssl/bio.h>
 
  int BIO_socket(int domain, int socktype, int protocol, int options);
+ int BIO_bind(int sock, const BIO_ADDR *addr, int options);
  int BIO_connect(int sock, const BIO_ADDR *addr, int options);
  int BIO_listen(int sock, const BIO_ADDR *addr, int options);
  int BIO_accept_ex(int accept_sock, BIO_ADDR *peer, int options);
@@ -21,6 +22,10 @@ BIO_socket() creates a socket in the domain B<domain>, of type
 B<socktype> and B<protocol>.  Socket B<options> are currently unused,
 but is present for future use.
 
+BIO_bind() binds the source address and service to a socket and
+may be useful before calling BIO_connect().  The options may include
+B<BIO_SOCK_REUSADDR>, which is described in L</FLAGS> below.
+
 BIO_connect() connects B<sock> to the address and service given by
 B<addr>.  Connection B<options> may be zero or any combination of
 B<BIO_SOCK_KEEPALIVE>, B<BIO_SOCK_NONBLOCK> and B<BIO_SOCK_NODELAY>.
@@ -81,7 +86,7 @@ BIO_socket() returns the socket number on success or B<INVALID_SOCKET>
 (-1) on error.  When an error has occurred, the OpenSSL error stack
 will hold the error data and errno has the system error.
 
-BIO_connect() and BIO_listen() return 1 on success or 0 on error.
+BIO_bind(), BIO_connect() and BIO_listen() return 1 on success or 0 on error.
 When an error has occurred, the OpenSSL error stack will hold the error
 data and errno has the system error.
 
@@ -102,7 +107,7 @@ L<BIO_ADDR(3)>
 
 =head1 COPYRIGHT
 
-Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the OpenSSL license (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index cffcd3debdfe1712b7e0a0ad9ef39d702a88e52b..c6f57f214cabcc526c9f00df6d0bc273c89bb84b 100644 (file)
@@ -709,6 +709,7 @@ int BIO_sock_info(int sock,
 
 int BIO_socket(int domain, int socktype, int protocol, int options);
 int BIO_connect(int sock, const BIO_ADDR *addr, int options);
+int BIO_bind(int sock, const BIO_ADDR *addr, int options);
 int BIO_listen(int sock, const BIO_ADDR *addr, int options);
 int BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options);
 int BIO_closesocket(int sock);
index 7a552d610543759d79e54d46165df573fe9e094e..4931c1d0ad434f2c47703d7c18f8a0918ccf4856 100644 (file)
@@ -24,6 +24,7 @@ int ERR_load_BIO_strings(void);
 # define BIO_F_BIO_ACCEPT                                 101
 # define BIO_F_BIO_ACCEPT_EX                              137
 # define BIO_F_BIO_ADDR_NEW                               144
+# define BIO_F_BIO_BIND                                   147
 # define BIO_F_BIO_CALLBACK_CTRL                          131
 # define BIO_F_BIO_CONNECT                                138
 # define BIO_F_BIO_CTRL                                   103
index b1dd6a09c6c19273d8890477d4436113560c822c..0049eabc06a1f2ad422926a160ad7aebef28846c 100644 (file)
@@ -4504,3 +4504,4 @@ RAND_DRBG_bytes                         4445      1_1_1   EXIST::FUNCTION:
 RAND_DRBG_secure_new                    4446   1_1_1   EXIST::FUNCTION:
 OSSL_STORE_vctrl                        4447   1_1_1   EXIST::FUNCTION:
 X509_get0_authority_key_id              4448   1_1_0h  EXIST::FUNCTION:
+BIO_bind                                4449   1_1_1   EXIST::FUNCTION:SOCK