]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#8847 Add SOCKET_BIND_ADDRESSES Option
authorHoweverAT <laeufer4321@gmx.at>
Fri, 19 Mar 2021 09:28:01 +0000 (10:28 +0100)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 25 Mar 2021 18:47:11 +0000 (18:47 +0000)
doc/man/man3/ldap_get_option.3
doc/man/man5/ldap.conf.5
include/ldap.h
libraries/libldap/init.c
libraries/libldap/ldap-int.h
libraries/libldap/open.c
libraries/libldap/options.c
libraries/libldap/os-ip.c
libraries/libldap/unbind.c
tests/progs/Makefile.in

index 28d0227316706da4b06e789894ede8ac8b27f412..023ef4be49d01bd5ad29b3af0c97df73247794ed 100644 (file)
@@ -376,6 +376,21 @@ must be a
 This is a read-only, handle-specific option.
 This option is OpenLDAP specific.
 .TP
+.B LDAP_OPT_SOCKET_BIND_ADDRESSES
+Sets/gets a space-separated list of IP Addresses used as binding interface
+to remote server when trying to establish a connection. Only one valid IPv4
+address and/or one valid IPv6 address are allowed in the list.
+.BR outvalue
+must be a
+.BR "char **",
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library duplicates the corresponding string.
+.TP
 .B LDAP_OPT_TIMELIMIT
 Sets/gets the value that defines the time limit after which
 a search operation should be terminated by the server.
index f77fcf18a122740b1d13475f2a50fffe96398f9e..17b71549919c82151dc6c21d7685713f26937826 100644 (file)
@@ -213,6 +213,11 @@ specifies a request for unlimited search size.  Please note that the server
 may still apply any server-side limit on the amount of entries that can be 
 returned by a search operation.
 .TP
+.B SOCKET_BIND_ADDRESSES <IP>
+Specifies the source bind IP to be used for connecting to target LDAP server.
+Multiple IP addresses must be space separated. Only one valid IPv4
+address and/or one valid IPv6 address are allowed in the list.
+.TP
 .B TIMELIMIT <integer>
 Specifies a time limit (in seconds) to use when performing searches.
 The number should be a non-negative integer.  \fITIMELIMIT\fP of zero (0)
index 8f45144e1193dddfa469a0328fc0f3604bd2c53b..aca579076156328cdc12563a915ad97f03b231fe 100644 (file)
@@ -132,6 +132,7 @@ LDAP_BEGIN_DECL
 #define        LDAP_OPT_CONNECT_CB                     0x5011  /* connection callbacks */
 #define        LDAP_OPT_SESSION_REFCNT         0x5012  /* session reference count */
 #define        LDAP_OPT_KEEPCONN               0x5013  /* keep the connection on read error or NoD */
+#define        LDAP_OPT_SOCKET_BIND_ADDRESSES  0x5014  /* user configured bind IPs */
 
 /* OpenLDAP TLS options */
 #define LDAP_OPT_X_TLS                         0x6000
index f2e3e5038411079b0f9895b510214075a5aab43d..ff9e5d49c28cbd17ccc99c03dacd8b8a0772b115 100644 (file)
@@ -38,6 +38,7 @@
 struct ldapoptions ldap_int_global_options =
        { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE
                LDAP_LDO_NULLARG
+               LDAP_LDO_SOURCEIP_NULLARG
                LDAP_LDO_CONNECTIONLESS_NULLARG
                LDAP_LDO_TLS_NULLARG
                LDAP_LDO_SASL_NULLARG
@@ -93,6 +94,7 @@ static const struct ol_attribute {
                offsetof(struct ldapoptions, ldo_defport)},
        {0, ATTR_OPTION,        "HOST",                 NULL,   LDAP_OPT_HOST_NAME}, /* deprecated */
        {0, ATTR_OPTION,        "URI",                  NULL,   LDAP_OPT_URI}, /* replaces HOST/PORT */
+       {0, ATTR_OPTION,        "SOCKET_BIND_ADDRESSES",        NULL,   LDAP_OPT_SOCKET_BIND_ADDRESSES},
        {0, ATTR_BOOL,          "REFERRALS",    NULL,   LDAP_BOOL_REFERRALS},
        {0, ATTR_INT,           "KEEPALIVE_IDLE",       NULL,   LDAP_OPT_X_KEEPALIVE_IDLE},
        {0, ATTR_INT,           "KEEPALIVE_PROBES",     NULL,   LDAP_OPT_X_KEEPALIVE_PROBES},
@@ -142,7 +144,7 @@ static const struct ol_attribute {
        {0, ATTR_NONE,          NULL,           NULL,   0}
 };
 
-#define MAX_LDAP_ATTR_LEN  sizeof("TLS_CIPHER_SUITE")
+#define MAX_LDAP_ATTR_LEN  sizeof("SOCKET_BIND_ADDRESSES")
 #define MAX_LDAP_ENV_PREFIX_LEN 8
 
 static int
@@ -519,6 +521,12 @@ ldap_int_destroy_global_options(void)
                ldap_free_urllist( gopts->ldo_defludp );
                gopts->ldo_defludp = NULL;
        }
+
+       if ( gopts->ldo_local_ip_addrs.local_ip_addrs ) {
+               LDAP_FREE( gopts->ldo_local_ip_addrs.local_ip_addrs );
+               gopts->ldo_local_ip_addrs.local_ip_addrs = NULL;
+       }
+
 #if defined(HAVE_WINSOCK) || defined(HAVE_WINSOCK2)
        WSACleanup( );
 #endif
@@ -558,6 +566,9 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl
        gopts->ldo_tm_api.tv_sec = -1;
        gopts->ldo_tm_net.tv_sec = -1;
 
+       memset( &gopts->ldo_local_ip_addrs, 0,
+               sizeof( gopts->ldo_local_ip_addrs ) );
+
        /* ldo_defludp will be freed by the termination handler
         */
        ldap_url_parselist(&gopts->ldo_defludp, "ldap://localhost/");
index 32ef6669221727a2cac9ce2f91c9f80edc5e2b99..e40518b32d48764fff510570c3790bfa6d076cd1 100644 (file)
@@ -45,9 +45,7 @@
 
 /* for struct timeval */
 #include <ac/time.h>
-#ifdef _WIN32
 #include <ac/socket.h>
-#endif
 
 #undef TV2MILLISEC
 #define TV2MILLISEC(tv) (((tv)->tv_sec * 1000) + ((tv)->tv_usec/1000))
@@ -199,6 +197,19 @@ typedef struct ldaplist {
        void *ll_data;
 } ldaplist;
 
+/*
+ * LDAP Client Source IP structure
+ */
+typedef struct ldapsourceip {
+       char    *local_ip_addrs;
+       struct in_addr  ip4_addr;
+       unsigned short  has_ipv4;
+#ifdef LDAP_PF_INET6
+       struct in6_addr ip6_addr;
+       unsigned short  has_ipv6;
+#endif
+} ldapsourceip;
+
 /*
  * structure representing get/set'able options
  * which have global defaults.
@@ -256,6 +267,15 @@ struct ldapoptions {
 
 #define LDAP_LDO_NULLARG       ,0,0,0,0 ,{0},{0} ,0,0,0,0, 0,0,0,0, 0,0, 0,0,0,0,0,0, 0, 0
 
+       /* LDAP user configured bind IPs */
+       struct ldapsourceip ldo_local_ip_addrs;
+
+#ifdef LDAP_PF_INET6
+#define LDAP_LDO_SOURCEIP_NULLARG      ,{0,0,0,0,0}
+#else
+#define LDAP_LDO_SOURCEIP_NULLARG      ,{0,0,0}
+#endif
+
 #ifdef LDAP_CONNECTIONLESS
 #define        LDAP_IS_UDP(ld)         ((ld)->ld_options.ldo_is_udp)
        void*                   ldo_peer;       /* struct sockaddr* */
@@ -727,6 +747,9 @@ LDAP_F (void) ldap_clear_select_write( LDAP *ld, Sockbuf *sb );
 LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb );
 LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb );
 
+LDAP_F (int) ldap_validate_and_fill_sourceip  ( char** source_ip_lst,
+       ldapsourceip* temp_source_ip );
+
 LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb,
        ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr );
 
index bdf24d12cccf74819dc237e6024ab9b27e8ce551..fdded08149629cf71f94db24090300f4cbb9bfc7 100644 (file)
@@ -207,6 +207,14 @@ ldap_create( LDAP **ldp )
 
        if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem;
 
+       ld->ld_options.ldo_local_ip_addrs.local_ip_addrs = NULL;
+       if( gopts->ldo_local_ip_addrs.local_ip_addrs ) {
+               ld->ld_options.ldo_local_ip_addrs.local_ip_addrs =
+                       LDAP_STRDUP( gopts->ldo_local_ip_addrs.local_ip_addrs );
+               if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs == NULL )
+                       goto nomem;
+       }
+
        ld->ld_lberoptions = LBER_USE_DER;
 
        ld->ld_sb = ber_sockbuf_alloc( );
index c6ec0adc6614af2cb8171d7faa0c12e68515502c..e07a4ce60b4a083d63fe39080a516243ebc1b3d2 100644 (file)
@@ -258,6 +258,17 @@ ldap_get_option(
                rc = LDAP_OPT_SUCCESS;
                break;
 
+       case LDAP_OPT_SOCKET_BIND_ADDRESSES:
+               if ( lo->ldo_local_ip_addrs.local_ip_addrs == NULL ) {
+                       * (void **) outvalue = NULL;
+               }
+               else {
+                       * (char **) outvalue =
+                               LDAP_STRDUP( lo->ldo_local_ip_addrs.local_ip_addrs );
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
+
        case LDAP_OPT_URI:
                * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp);
                rc = LDAP_OPT_SUCCESS;
@@ -594,6 +605,43 @@ ldap_set_option(
                        break;
                }
 
+       case LDAP_OPT_SOCKET_BIND_ADDRESSES: {
+                       const char *source_ip = (const char *) invalue;
+                       char **source_ip_lst = NULL;
+
+                       ldapsourceip temp_source_ip;
+                       memset( &temp_source_ip, 0, sizeof( ldapsourceip ) );
+                       rc = LDAP_OPT_SUCCESS;
+                       if( source_ip == NULL ) {
+                               if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) {
+                                       LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs );
+                                       memset( &ld->ld_options.ldo_local_ip_addrs, 0,
+                                               sizeof( ldapsourceip ) );
+                               }
+                       }
+                       else {
+                               source_ip_lst = ldap_str2charray( source_ip, " " );
+
+                               if ( source_ip_lst == NULL )
+                                       rc =  LDAP_NO_MEMORY;
+
+                               if( rc == LDAP_OPT_SUCCESS ) {
+                                       rc = ldap_validate_and_fill_sourceip ( source_ip_lst,
+                                               &temp_source_ip );
+                                       ldap_charray_free( source_ip_lst );
+                               }
+                               if ( rc == LDAP_OPT_SUCCESS ) {
+                                       if ( lo->ldo_local_ip_addrs.local_ip_addrs != NULL ) {
+                                               LDAP_FREE( lo->ldo_local_ip_addrs.local_ip_addrs );
+                                               lo->ldo_local_ip_addrs.local_ip_addrs = NULL;
+                                       }
+                                       lo->ldo_local_ip_addrs = temp_source_ip;
+                                       lo->ldo_local_ip_addrs.local_ip_addrs = LDAP_STRDUP( source_ip );
+                               }
+                       }
+                       break;
+               }
+
        case LDAP_OPT_URI: {
                        const char *urls = (const char *) invalue;
                        LDAPURLDesc *ludlist = NULL;
index bae97c57a22ccf48d8f10f250338c6beb513e4f1..14899cc00791191f03c3ef69dec18ab64e834bec 100644 (file)
@@ -475,6 +475,43 @@ ldap_pvt_inet_aton( const char *host, struct in_addr *in)
 }
 #endif
 
+int
+ldap_validate_and_fill_sourceip  (char** source_ip_lst, ldapsourceip* temp_source_ip )
+{
+       int i = 0;
+       int rc = LDAP_PARAM_ERROR;
+
+       for ( i = 0; source_ip_lst[i] != NULL; i++ ) {
+               Debug1( LDAP_DEBUG_TRACE,
+                               "ldap_validate_and_fill_sourceip(%s)\n",
+                               source_ip_lst[i] );
+
+               if ( !temp_source_ip->has_ipv4 ) {
+                       if ( inet_aton( source_ip_lst[i], &temp_source_ip->ip4_addr ) ) {
+                               temp_source_ip->has_ipv4 = 1;
+                               rc = LDAP_OPT_SUCCESS;
+                               continue;
+                       }
+               }
+#ifdef LDAP_PF_INET6
+               if ( !temp_source_ip->has_ipv6 ) {
+                       if ( inet_pton( AF_INET6, source_ip_lst[i],
+                               & temp_source_ip->ip6_addr ) ) {
+                               temp_source_ip->has_ipv6 = 1;
+                               rc = LDAP_OPT_SUCCESS;
+                               continue;
+                       }
+               }
+#endif
+               memset( temp_source_ip, 0, sizeof( * (temp_source_ip ) ) );
+               Debug1( LDAP_DEBUG_TRACE,
+                               "ldap_validate_and_fill_sourceip: validation failed for (%s)\n",
+                               source_ip_lst[i] );
+               break;
+       }
+       return rc;
+}
+
 int
 ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr)
 {
@@ -607,6 +644,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
        rc = -1;
 
        for( sai=res; sai != NULL; sai=sai->ai_next) {
+               unsigned short bind_success = 1;
                if( sai->ai_addr == NULL ) {
                        Debug0(LDAP_DEBUG_TRACE,
                                "ldap_connect_to_host: getaddrinfo "
@@ -638,6 +676,24 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
                                Debug2(LDAP_DEBUG_TRACE,
                                      "ldap_connect_to_host: Trying %s %s\n",
                                        addr, serv );
+                               if( ld->ld_options.ldo_local_ip_addrs.has_ipv6 ) {
+                                       struct sockaddr_in6 ip6addr;
+                                       char bind_addr[INET6_ADDRSTRLEN];
+                                       ip6addr.sin6_family = AF_INET6;
+                                       ip6addr.sin6_addr = ld->ld_options.ldo_local_ip_addrs.ip6_addr;
+                                       inet_ntop( AF_INET6,
+                                               &(ip6addr.sin6_addr),
+                                               bind_addr, sizeof bind_addr );
+                                       Debug1( LDAP_DEBUG_TRACE,
+                                               "ldap_connect_to_host: From source address %s\n",
+                                               bind_addr );
+                                       if ( bind( s, ( struct sockaddr* ) &ip6addr, sizeof ip6addr ) != 0 ) {
+                                               Debug1( LDAP_DEBUG_TRACE,
+                                                               "ldap_connect_to_host: Failed to bind source address %s\n",
+                                                               bind_addr );
+                                               bind_success = 0;
+                                       }
+                               }
                        } break;
 #endif
                        case AF_INET: {
@@ -648,17 +704,36 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
                                Debug2(LDAP_DEBUG_TRACE,
                                      "ldap_connect_to_host: Trying %s:%s\n",
                                        addr, serv );
+                               if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) {
+                                       struct sockaddr_in ip4addr;
+                                       char bind_addr[INET_ADDRSTRLEN];
+                                       ip4addr.sin_family = AF_INET;
+                                       ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr;
+                                       inet_ntop( AF_INET,
+                                               &(ip4addr.sin_addr),
+                                               bind_addr, sizeof bind_addr );
+                                       Debug1( LDAP_DEBUG_TRACE,
+                                               "ldap_connect_to_host: From source address %s\n",
+                                               bind_addr );
+                                       if ( bind(s, ( struct sockaddr* )&ip4addr, sizeof ip4addr ) != 0 ) {
+                                               Debug1( LDAP_DEBUG_TRACE,
+                                                               "ldap_connect_to_host: Failed to bind source address %s\n",
+                                                               bind_addr );
+                                               bind_success = 0;
+                                       }
+                               }
                        } break;
                }
-
-               rc = ldap_pvt_connect( ld, s,
-                       sai->ai_addr, sai->ai_addrlen, async );
-               if ( rc == 0 || rc == -2 ) {
-                       err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
-                       if ( err )
-                               rc = err;
-                       else
-                               break;
+               if ( bind_success ) {
+                       rc = ldap_pvt_connect( ld, s,
+                               sai->ai_addr, sai->ai_addrlen, async );
+                       if ( rc == 0 || rc == -2 ) {
+                               err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
+                               if ( err )
+                                       rc = err;
+                               else
+                                       break;
+                       }
                }
                ldap_pvt_close_socket(ld, s);
        }
@@ -687,7 +762,14 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
        rc = s = -1;
        for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
                struct sockaddr_in      sin;
-
+               unsigned short bind_success = 1;
+#ifdef HAVE_INET_NTOA_B
+               char address[INET_ADDR_LEN];
+               char bind_addr[INET_ADDR_LEN];
+#else
+               char *address;
+               char *bind_addr;
+#endif
                s = ldap_int_socket( ld, PF_INET, socktype );
                if ( s == AC_SOCKET_INVALID ) {
                        /* use_hp ? continue : break; */
@@ -712,30 +794,45 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
                }
 
 #ifdef HAVE_INET_NTOA_B
-               {
-                       /* for VxWorks */
-                       char address[INET_ADDR_LEN];
-                       inet_ntoa_b(sin.sin_address, address);
-                       Debug2(LDAP_DEBUG_TRACE,
-                             "ldap_connect_to_host: Trying %s:%d\n",
-                               address, port );
-               }
+               /* for VxWorks */
+               inet_ntoa_b( sin.sin_address, address );
 #else
-               Debug2(LDAP_DEBUG_TRACE,
-                     "ldap_connect_to_host: Trying %s:%d\n",
-                       inet_ntoa(sin.sin_addr), port );
+               address = inet_ntoa( sin.sin_addr );
 #endif
-
-               rc = ldap_pvt_connect(ld, s,
-                       (struct sockaddr *)&sin, sizeof(sin),
-                       async);
-   
-               if ( (rc == 0) || (rc == -2) ) {
-                       int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
-                       if ( err )
-                               rc = err;
-                       else
-                               break;
+               Debug2( LDAP_DEBUG_TRACE,
+                       "ldap_connect_to_host: Trying %s:%d\n",
+                       address, port );
+               if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) {
+                       struct sockaddr_in ip4addr;
+                       ip4addr.sin_family = AF_INET;
+                       ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr;
+#ifdef HAVE_INET_NTOA_B
+                       inet_ntoa_b( ip4addr.sin_address, bind_addr );
+#else
+                       bind_addr = inet_ntoa( ip4addr.sin_addr );
+#endif
+                       Debug1( LDAP_DEBUG_TRACE,
+                               "ldap_connect_to_host: From source address %s\n",
+                               bind_addr );
+                       if ( bind( s, (struct sockaddr*)&ip4addr, sizeof ip4addr ) != 0 ) {
+                               Debug1( LDAP_DEBUG_TRACE,
+                                               "ldap_connect_to_host: Failed to bind source address %s\n",
+                                               bind_addr );
+                               bind_success = 0;
+                       }
+               }
+               if ( bind_success ) {
+                       rc = ldap_pvt_connect(ld, s,
+                                       (struct sockaddr *)&sin, sizeof(sin),
+                                       async);
+
+                       if ( (rc == 0) || (rc == -2) ) {
+                               int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
+                               if ( err )
+                                       rc = err;
+                               else
+                                       break;
+                       }
                }
 
                ldap_pvt_close_socket(ld, s);
index 754ea4d7b7c472ef4bd4f56ad2b64f5284563663..51f85b81621b5cfe970b2dc9f715a65bff06b06e 100644 (file)
@@ -175,6 +175,12 @@ ldap_ld_free(
                ld->ld_options.ldo_defludp = NULL;
        }
 
+       if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) {
+               LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs );
+               memset( & ld->ld_options.ldo_local_ip_addrs, 0,
+                       sizeof( ldapsourceip ) );
+       }
+
 #ifdef LDAP_CONNECTIONLESS
        if ( ld->ld_options.ldo_peer != NULL ) {
                LDAP_FREE( ld->ld_options.ldo_peer );
index d947e8db4472f0cb7f3a8d968951680233949ec1..13f1e8be2c6a9335e6dacb540658991488919961 100644 (file)
@@ -57,7 +57,7 @@ slapd-bind: slapd-bind.o $(OBJS) $(XLIBS)
        $(LTLINK) -o $@ slapd-bind.o $(OBJS) $(LIBS)
 
 ldif-filter: ldif-filter.o $(XLIBS)
-       $(LTLINK) -o $@ ldif-filter.o $(LIBS)
+       $(LTLINK) -o $@ ldif-filter.o $(OBJS) $(LIBS)
 
 slapd-mtread: slapd-mtread.o $(OBJS) $(XLIBS)
        $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(LIBS)