[Date Prev][Date Next] [Chronological] [Thread] [Top]

libldap: too little space allocated for IPv6 address *patch* (ITS#2018)



Full_Name: Thorild Selén
Version: 2.0.23 and HEAD
OS: Linux 2.4.19 (Debian)
URL: 
Submission from: (NULL) (130.238.19.17)


(In brief: IPv6 address allocation problem; patch at end)

I was using strace to examine a program that kept hanging, and saw:

> connect(10, {sin_family=AF_INET6, sin6_port=htons(389),
>       inet_pton(AF_INET6, "3ffe:200:66:8000::11", &sin6_addr),
>       sin6_flowinfo=0 , sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation
>       now in progress)
> select(1024, NULL, [10], NULL, {16, 0}) = 1 (out [10], left {16, 0})

(We connect to the LDAP server. All is well so far. From the data that follows,
this looks like libnss_ldap checking which groups exist on the system.)
And then:

> getpeername(10, {sin_family=AF_INET6, sin6_port=htons(389),
>       inet_pton(AF_INET6, "3ffe:200:66:8000:1cd3:ffbf:ac7c:5940",
>       &sin6_addr), sin6_flowinfo=0, sin6_scope_id=137790328}, [28]) = 0
> fcntl64(10, F_GETFL)                    = 0x802 (flags O_RDWR|O_NONBLOCK)
> fcntl64(10, F_SETFL, O_RDWR)            = 0
> getpeername(10, {sin_family=AF_INET6, sin6_port=htons(389),
>       inet_pton(AF_INET6, "3ffe:200:66:8000:2cd4:ffbf:af7a:5840",
>       &sin6_addr), sin6_flowinfo=0, sin6_scope_id=139343664}, [28]) = 0

The second parts of these addresses are mangled; something isn't allocating
enough space to store an IPv6 address. That made me look in the libldap
source for a bug of this kind, and saw that in libraries/libldap/os-ip.c,
in ldap_host_connected_to, an auto struct sockaddr is used to store an
address; possibly an IPv6 address, which won't fit; a struct
sockaddr_storage should be used instead (see RFC 2553). This bug is present
in this version as well as in the present working version (HEAD).

Below is a patch for HEAD. 

/Thorild


Patch copyright 2002 Thorild Selén, all rights reserved.
Contains code copyright 1999-2001 the OpenLDAP Foundation,
 Redwood City, California, USA.
This is free software; you can redistribute and use it
under the same terms as OpenLDAP itself.

=========================================================


diff -Naur ldap/libraries/libldap/os-ip.c ldap-new/libraries/libldap/os-ip.c
--- ldap/libraries/libldap/os-ip.c	Sun Jul 28 03:30:39 2002
+++ ldap-new/libraries/libldap/os-ip.c	Fri Aug  9 17:50:51 2002
@@ -1,4 +1,4 @@
-/* $OpenLDAP: pkg/ldap/libraries/libldap/os-ip.c,v 1.79 2002/07/28 03:30:39
kurt Exp $ */
+/* $OpenLDAP: /libraries/libldap/os-ip.c,v 1.72.2.4 2002/07/28 19:10:46 kurt
Exp $ */
 /*
  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
@@ -482,7 +482,13 @@
 {
 	struct hostent	*hp;
 	socklen_t		len;
-	struct sockaddr	sa;
+#ifdef LDAP_PF_INET6
+	struct sockaddr_storage	sa_storage;
+#else
+	struct sockaddr         sa_storage;
+#endif
+	struct sockaddr *sa = (struct sockaddr *) &sa_storage;
+
 	char			*addr;
 	char			*host;
 
@@ -492,11 +498,11 @@
    	char			*ha_buf=NULL;
 	ber_socket_t	sd;
 
-	(void)memset( (char *)&sa, '\0', sizeof( struct sockaddr ));
-	len = sizeof( sa );
+	(void)memset( (char *)&sa_storage, '\0', sizeof( sa_storage ));
+	len = sizeof( sa_storage );
 
 	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
-	if ( getpeername( sd, &sa, &len ) == -1 ) {
+	if ( getpeername( sd, sa, &len ) == -1 ) {
 		return( NULL );
 	}
 
@@ -506,19 +512,19 @@
 	* hostname is used as the kerberos instance.
 	*/
 
-	switch (sa.sa_family) {
+	switch (sa->sa_family) {
 #ifdef LDAP_PF_LOCAL
 	case AF_LOCAL:
 		return LDAP_STRDUP( ldap_int_hostname );
 #endif
 #ifdef LDAP_PF_INET6
 	case AF_INET6:
-		addr = (char *) &((struct sockaddr_in6 *)&sa)->sin6_addr;
+		addr = (char *) &((struct sockaddr_in6 *)sa)->sin6_addr;
 		len = sizeof( struct in6_addr );
 		break;
 #endif
 	case AF_INET:
-		addr = (char *) &((struct sockaddr_in *)&sa)->sin_addr;
+		addr = (char *) &((struct sockaddr_in *)sa)->sin_addr;
 		len = sizeof( struct in_addr );
 
 		{
@@ -526,7 +532,7 @@
 			localhost.sin_addr.s_addr = htonl( INADDR_ANY );
 
 			if( memcmp ( &localhost.sin_addr,
-				&((struct sockaddr_in *)&sa)->sin_addr,
+				&((struct sockaddr_in *)sa)->sin_addr,
 				sizeof(localhost.sin_addr) ) == 0 )
 			{
 				return LDAP_STRDUP( ldap_int_hostname );
@@ -536,7 +542,7 @@
 			localhost.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
 
 			if( memcmp ( &localhost.sin_addr,
-				&((struct sockaddr_in *)&sa)->sin_addr,
+				&((struct sockaddr_in *)sa)->sin_addr,
 				sizeof(localhost.sin_addr) ) == 0 )
 			{
 				return LDAP_STRDUP( ldap_int_hostname );
@@ -552,7 +558,7 @@
 
 	host = NULL;
 	if ((ldap_pvt_gethostbyaddr_a( addr, len,
-		sa.sa_family, &he_buf, &ha_buf,
+		sa->sa_family, &he_buf, &ha_buf,
 		&hp,&local_h_errno ) == 0 ) &&
 		(hp != NULL) && ( hp->h_name != NULL ) )
 	{