From 97e6225cc9414e028e5e5066f0eb29b57219cf47 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Fri, 15 Dec 2006 01:11:11 +0000 Subject: [PATCH] implement full IPv6 support in ACLs; use URL notation (as suggested by Howard) to disambiguate parsing (ITS#4756) --- servers/slapd/acl.c | 56 +++++++++++++++++++++++++++++++++++++++- servers/slapd/aclparse.c | 55 +++++++++++++++++++++++++++++++++++++++ servers/slapd/daemon.c | 16 ++++++------ servers/slapd/slap.h | 8 ++++++ 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 8a4c43e496..39857052f8 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -40,6 +40,9 @@ #define ACL_BUF_SIZE 1024 /* use most appropriate size */ static const struct berval acl_bv_ip_eq = BER_BVC( "IP=" ); +#ifdef LDAP_PF_INET6 +static const struct berval acl_bv_ipv6_eq = BER_BVC( "IP=[" ); +#endif /* LDAP_PF_INET6 */ #ifdef LDAP_PF_LOCAL static const struct berval acl_bv_path_eq = BER_BVC("PATH="); #endif /* LDAP_PF_LOCAL */ @@ -242,7 +245,6 @@ slap_access_allowed( } } -vd_access: control = slap_acl_mask( a, &mask, op, e, desc, val, MAXREMATCHES, matches, count, state ); @@ -1317,6 +1319,58 @@ slap_acl_mask( if ( (addr & b->a_peername_mask) != b->a_peername_addr ) continue; +#ifdef LDAP_PF_INET6 + /* extract IPv6 and try exact match */ + } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) { + char *port; + char buf[] = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; + struct berval ip; + struct in6_addr addr; + int port_number = -1, i; + + if ( strncasecmp( op->o_conn->c_peer_name.bv_val, + acl_bv_ipv6_eq.bv_val, + acl_bv_ipv6_eq.bv_len ) != 0 ) + continue; + + ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len; + ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len; + + port = strrchr( ip.bv_val, ']' ); + if ( port ) { + ip.bv_len = port - ip.bv_val; + ++port; + if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 ) + continue; + } + + /* the port check can be anticipated here */ + if ( b->a_peername_port != -1 && port_number != b->a_peername_port ) + continue; + + /* address longer than expected? */ + if ( ip.bv_len >= sizeof(buf) ) + continue; + + AC_MEMCPY( buf, ip.bv_val, ip.bv_len ); + buf[ ip.bv_len ] = '\0'; + + if ( inet_pton( AF_INET6, buf, &addr ) != 1 ) + continue; + + /* check mask */ + for ( i = 0; i < 4; i++ ) { + if ( ( addr.s6_addr32[i] & b->a_peername_mask6.s6_addr32[i] ) + != b->a_peername_addr6.s6_addr32[i] ) + { + break; + } + } + + if ( i != 4 ) + continue; +#endif /* LDAP_PF_INET6 */ + #ifdef LDAP_PF_LOCAL /* extract path and try exact match */ } else if ( b->a_peername_style == ACL_STYLE_PATH ) { diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c index d5bdf1b6e1..e151d10e81 100644 --- a/servers/slapd/aclparse.c +++ b/servers/slapd/aclparse.c @@ -804,6 +804,14 @@ parse_acl( } else if ( strcasecmp( style, "ip" ) == 0 ) { sty = ACL_STYLE_IP; + } else if ( strcasecmp( style, "ipv6" ) == 0 ) { +#ifndef LDAP_PF_INET6 + Debug( LDAP_DEBUG_ANY, + "%s: line %d: IPv6 not supported\n", + fname, lineno, 0 ); +#endif /* ! LDAP_PF_INET6 */ + sty = ACL_STYLE_IPV6; + } else if ( strcasecmp( style, "path" ) == 0 ) { sty = ACL_STYLE_PATH; #ifndef LDAP_PF_LOCAL @@ -1301,6 +1309,7 @@ parse_acl( case ACL_STYLE_EXPAND: /* cheap replacement to regex for simple expansion */ case ACL_STYLE_IP: + case ACL_STYLE_IPV6: case ACL_STYLE_PATH: /* legal, peername specific */ break; @@ -1384,6 +1393,52 @@ parse_acl( goto fail; } } + +#ifdef LDAP_PF_INET6 + } else if ( sty == ACL_STYLE_IPV6 ) { + char *addr = NULL, + *mask = NULL, + *port = NULL; + + split( right, '{', &addr, &port ); + split( addr, '%', &addr, &mask ); + + if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) { + /* illegal address */ + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "illegal peername address \"%s\".\n", + fname, lineno, addr ); + goto fail; + } + + if ( mask == NULL ) { + mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; + } + + if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) { + /* illegal mask */ + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "illegal peername address mask " + "\"%s\".\n", + fname, lineno, mask ); + goto fail; + } + + b->a_peername_port = -1; + if ( port ) { + char *end = NULL; + + b->a_peername_port = strtol( port, &end, 10 ); + if ( end == port || end[0] != '}' ) { + /* illegal port */ + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "illegal peername port specification " + "\"{%s}\".\n", + fname, lineno, port ); + goto fail; + } + } +#endif /* LDAP_PF_INET6 */ } } continue; diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c index ccafda3a12..fb07060016 100644 --- a/servers/slapd/daemon.c +++ b/servers/slapd/daemon.c @@ -1388,9 +1388,9 @@ slap_open_listener( inet_ntop( AF_INET6, &((struct sockaddr_in6 *)*sal)->sin6_addr, addr, sizeof addr); port = ntohs( ((struct sockaddr_in6 *)*sal)->sin6_port ); - l.sl_name.bv_len = strlen(addr) + sizeof("IP= 65535"); + l.sl_name.bv_len = strlen(addr) + sizeof("IP=[]:65535"); l.sl_name.bv_val = ber_memalloc( l.sl_name.bv_len ); - snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=%s %d", + snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=[%s]:%d", addr, port ); l.sl_name.bv_len = strlen( l.sl_name.bv_val ); } break; @@ -1595,7 +1595,7 @@ slap_listener( #ifdef LDAP_PF_LOCAL char peername[MAXPATHLEN + sizeof("PATH=")]; #elif defined(LDAP_PF_INET6) - char peername[sizeof("IP=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535")]; + char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")]; #else /* ! LDAP_PF_LOCAL && ! LDAP_PF_INET6 */ char peername[sizeof("IP=255.255.255.255:65336")]; #endif /* LDAP_PF_LOCAL */ @@ -1759,7 +1759,7 @@ slap_listener( peeraddr = (char *) inet_ntop( AF_INET6, &from.sa_in6_addr.sin6_addr, addr, sizeof addr ); - sprintf( peername, "IP=%s %d", + sprintf( peername, "IP=[%s]:%d", peeraddr != NULL ? peeraddr : SLAP_STRING_UNKNOWN, (unsigned) ntohs( from.sa_in6_addr.sin6_port ) ); } @@ -1767,10 +1767,10 @@ slap_listener( # endif /* LDAP_PF_INET6 */ case AF_INET: - peeraddr = inet_ntoa( from.sa_in_addr.sin_addr ); - sprintf( peername, "IP=%s:%d", - peeraddr != NULL ? peeraddr : SLAP_STRING_UNKNOWN, - (unsigned) ntohs( from.sa_in_addr.sin_port ) ); + peeraddr = inet_ntoa( from.sa_in_addr.sin_addr ); + sprintf( peername, "IP=%s:%d", + peeraddr != NULL ? peeraddr : SLAP_STRING_UNKNOWN, + (unsigned) ntohs( from.sa_in_addr.sin_port ) ); break; default: diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index e985b78519..091ab673f9 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1231,6 +1231,7 @@ typedef enum slap_style_e { ACL_STYLE_USERS, ACL_STYLE_SELF, ACL_STYLE_IP, + ACL_STYLE_IPV6, ACL_STYLE_PATH } slap_style_t; @@ -1398,8 +1399,15 @@ typedef struct slap_access { /* connection related stuff */ slap_style_t a_peername_style; struct berval a_peername_pat; +#ifdef LDAP_PF_INET6 + struct in6_addr a_peername_addr6, + a_peername_mask6; +#define a_peername_addr a_peername_addr6.s6_addr32[0] +#define a_peername_mask a_peername_mask6.s6_addr32[0] +#else /* ! LDAP_PF_INET6 */ unsigned long a_peername_addr, a_peername_mask; +#endif /* ! LDAP_PF_INET6 */ int a_peername_port; slap_style_t a_sockname_style; -- 2.39.5