ITS#8847 Add SOCKET_BIND_ADDRESSES Option

This commit is contained in:
HoweverAT 2021-03-19 10:28:01 +01:00 committed by Quanah Gibson-Mount
parent 8ebd065048
commit 9d594a118e
10 changed files with 249 additions and 35 deletions

View 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.

View 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)

View 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

View 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/");

View 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 );

View 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( );

View 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;

View 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
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);
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;
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);

View 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 );

View 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)