From b791dc9fbd1b88d033d848e3cb0db8f93e2dbff5 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Wed, 9 May 2012 16:03:59 -0400 Subject: [PATCH 374/375] Ticket #196 - RFE: Interpret IPV6 addresses for ACIs, replication, and chaining Bug Description: replication, chaining, and access control do not hanbdle IPv6 addresses Fix Description: For replication and chaining, we just needed to put brackets "[]" around the IP address in the ldap urls. We also need to update convert_to_openldap_uri(), which is called by slapi_ldap_init_ext(). Access control needed to remove the IPv6 restriction check, and update libaccess to be IPv6 aware. https://fedorahosted.org/389/ticket/196 Reviewed by: Norkio! (cherry picked from commit 4d7d59e756d5dc2dbc04c7ee95759f211795a093) (cherry picked from commit 44413ce12710f310d377727399abc8f9a870c5eb) --- ldap/servers/plugins/acl/acllas.c | 53 +-- ldap/servers/plugins/replication/repl5_plugins.c | 7 +- ldap/servers/plugins/replication/windows_private.c | 7 +- ldap/servers/slapd/ldaputil.c | 90 +++-- ldap/servers/slapd/slapi-plugin.h | 9 + lib/libaccess/lasip.cpp | 397 ++++++++++++++++----- lib/libaccess/lasip.h | 3 +- 7 files changed, 407 insertions(+), 159 deletions(-) diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c index fc7f185..a0cc53d 100644 --- a/ldap/servers/plugins/acl/acllas.c +++ b/ldap/servers/plugins/acl/acllas.c @@ -281,16 +281,12 @@ int DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth, void *arg) { + struct acl_pblock *aclpb = NULL; + PRNetAddr *client_praddr = NULL; + char ip_str[256]; + int rv = LAS_EVAL_TRUE; - struct acl_pblock *aclpb = NULL; - IPAddr_t ip=0; - PRNetAddr client_praddr; - struct in_addr client_addr; - int rv; - - - rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb, - subject, resource, auth_info, global_auth); + rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb, subject, resource, auth_info, global_auth); if ( rv != LAS_EVAL_TRUE || ( NULL == aclpb )) { acl_print_acllib_err(errp, NULL); slapi_log_error( SLAPI_LOG_ACL, plugin_name, @@ -298,29 +294,34 @@ DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t return LAS_EVAL_FAIL; } - if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR, - &client_praddr ) != 0 ) { - slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" ); - return( LAS_EVAL_FAIL ); - } - - if ( !PR_IsNetAddrType(&client_praddr, PR_IpAddrV4Mapped) ) { - slapi_log_error( SLAPI_LOG_ACL, plugin_name, - "Client address is IPv6. ACLs only support IPv4 addresses so far.\n"); + client_praddr = (PRNetAddr *)slapi_ch_malloc(sizeof(PRNetAddr)); + if(client_praddr == NULL){ + slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "DS_LASIpGetter: failed to allocate client_praddr\n"); return( LAS_EVAL_FAIL ); } - - client_addr.s_addr = client_praddr.ipv6.ip.pr_s6_addr32[3]; - ip = (IPAddr_t) ntohl( client_addr.s_addr ); - rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)ip, NULL); + if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR, client_praddr ) != 0 ) { + slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "DS_LASIpGetter: Could not get client IP.\n" ); + slapi_ch_free((void **)&client_praddr); + return( LAS_EVAL_FAIL ); + } - slapi_log_error( SLAPI_LOG_ACL, plugin_name, - "Returning client ip address '%s'\n", - (slapi_is_loglevel_set(SLAPI_LOG_ACL) ? inet_ntoa(client_addr) : "")); + rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)client_praddr, NULL); + if (rv < 0) { + slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: " + "Couldn't set the client addr property(%d)\n", rv ); + slapi_ch_free((void **)&client_praddr); + return LAS_EVAL_FAIL; + } + if( PR_NetAddrToString(client_praddr, ip_str, sizeof(ip_str)) == PR_SUCCESS){ + slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: " + "Returning client ip address '%s'\n", ip_str); + } else { + slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: " + "Returning client ip address 'unknown'\n"); + } return LAS_EVAL_TRUE; - } /* diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c index afb0364..dddbf15 100644 --- a/ldap/servers/plugins/replication/repl5_plugins.c +++ b/ldap/servers/plugins/replication/repl5_plugins.c @@ -133,7 +133,12 @@ multimaster_set_local_purl() } else { - local_purl = slapi_ch_smprintf("ldap://%s:%s", host, port); + if(slapi_is_ipv6_addr(host)){ + /* need to put brackets around the ipv6 address */ + local_purl = slapi_ch_smprintf("ldap://[%s]:%s", host, port); + } else { + local_purl = slapi_ch_smprintf("ldap://%s:%s", host, port); + } } /* slapi_ch_free acceptS NULL pointer */ diff --git a/ldap/servers/plugins/replication/windows_private.c b/ldap/servers/plugins/replication/windows_private.c index a103cad..4381943 100644 --- a/ldap/servers/plugins/replication/windows_private.c +++ b/ldap/servers/plugins/replication/windows_private.c @@ -321,7 +321,12 @@ const char* windows_private_get_purl(const Repl_Agmt *ra) char *hostname; hostname = agmt_get_hostname(ra); - windows_purl = slapi_ch_smprintf("ldap://%s:%d", hostname, agmt_get_port(ra)); + if(slapi_is_ipv6_addr(hostname)){ + /* need to put brackets around the ipv6 address */ + windows_purl = slapi_ch_smprintf("ldap://[%s]:%d", hostname, agmt_get_port(ra)); + } else { + windows_purl = slapi_ch_smprintf("ldap://%s:%d", hostname, agmt_get_port(ra)); + } slapi_ch_free_string(&hostname); return windows_purl; diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c index 331dd71..4b8c16d 100644 --- a/ldap/servers/slapd/ldaputil.c +++ b/ldap/servers/slapd/ldaputil.c @@ -165,55 +165,59 @@ convert_to_openldap_uri(const char *hostname_or_uri, int port, const char *proto char *my_copy = NULL; char *start = NULL; char *iter = NULL; + char *ptr = NULL; char *s = NULL; const char *brkstr = " "; + int done = 0; if (!hostname_or_uri) { - return NULL; + return NULL; + } + + if(slapi_is_ipv6_addr(hostname_or_uri)){ + /* We need to encapsulate the ipv6 addr with brackets */ + my_copy = slapi_ch_smprintf("[%s]",hostname_or_uri); + } else { + my_copy = slapi_ch_strdup(hostname_or_uri); } - my_copy = slapi_ch_strdup(hostname_or_uri); /* see if hostname_or_uri is an ldap uri */ if (!proto && !PL_strncasecmp(my_copy, "ldap", 4)) { - start = my_copy + 4; - if ((*start == 's') || (*start == 'i')) { - start++; - } - if (!PL_strncmp(start, "://", 3)) { - *start = '\0'; - proto = my_copy; - start += 3; - } else { - slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri", - "The given LDAP URI [%s] is not valid\n", hostname_or_uri); - goto end; - } + start = my_copy + 4; + if ((*start == 's') || (*start == 'i')) { + start++; + } + if (!PL_strncmp(start, "://", 3)) { + *start = '\0'; + proto = my_copy; + start += 3; + } else { + slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri", + "The given LDAP URI [%s] is not valid\n", hostname_or_uri); + goto end; + } } else if (!proto) { - slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri", - "The given LDAP URI [%s] is not valid\n", hostname_or_uri); - goto end; + slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri", + "The given LDAP URI [%s] is not valid\n", hostname_or_uri); + goto end; } else { - start = my_copy; /* just assume it's not a uri */ + start = my_copy; /* just assume it's not a uri */ } - for (s = ldap_utf8strtok_r(my_copy, brkstr, &iter); s != NULL; - s = ldap_utf8strtok_r(NULL, brkstr, &iter)) { - char *ptr; - int last = 0; - /* strtok will grab the '/' at the end of the uri, if any, - so terminate parsing there */ - if ((ptr = strchr(s, '/'))) { - *ptr = '\0'; - last = 1; - } - if (retstr) { - retstr = PR_sprintf_append(retstr, "/ %s://%s", proto, s); - } else { - retstr = PR_smprintf("%s://%s", proto, s); - } - if (last) { - break; - } + for (s = ldap_utf8strtok_r(my_copy, brkstr, &iter); s != NULL; s = ldap_utf8strtok_r(NULL, brkstr, &iter)) { + /* strtok will grab the '/' at the end of the uri, if any, so terminate parsing there */ + if ((ptr = strchr(s, '/'))) { + *ptr = '\0'; + done = 1; + } + if (retstr) { + retstr = PR_sprintf_append(retstr, "/ %s://%s", proto, s); + } else { + retstr = PR_smprintf("%s://%s", proto, s); + } + if (done) { + break; + } } /* add the port on the last one */ @@ -2271,3 +2275,15 @@ mozldap_ldap_explode_rdn( const char *rdn, const int notypes ) return( mozldap_ldap_explode( rdn, notypes, LDAP_RDN ) ); } +int +slapi_is_ipv6_addr( const char *hostname ){ + PRNetAddr addr; + + if(PR_StringToNetAddr(hostname, &addr) == PR_SUCCESS && + !PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) && + addr.raw.family == PR_AF_INET6) + { + return 1; + } + return 0; +} diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 75d5aa9..cb15a25 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -3015,6 +3015,15 @@ void slapi_rdn_set_rdn(Slapi_RDN *rdn,const Slapi_RDN *fromrdn); void slapi_rdn_free(Slapi_RDN **rdn); /** + * Checks if the value of ipAddress is a IPv6 address + * + * \param ipAddress is a string that is either an IPv4 or IPv6 address + * \return 1 if address is an IPv6 address + * \return 0 if address is an IPv4 address + */ +int slapi_is_ipv6_addr( const char *ipAddress); + +/** * Frees and clears the contents of a \c Slapi_RDN structure from memory. * * Both the RDN value and the array of split RDNs are freed. Those pointers diff --git a/lib/libaccess/lasip.cpp b/lib/libaccess/lasip.cpp index 7f5c01e..e66f48a 100644 --- a/lib/libaccess/lasip.cpp +++ b/lib/libaccess/lasip.cpp @@ -60,6 +60,8 @@ #include "aclcache.h" #include #include +#include +#include "nspr.h" #define LAS_IP_IS_CONSTANT(x) (((x) == (LASIpTree_t *)LAS_EVAL_TRUE) || ((x) == (LASIpTree_t *)LAS_EVAL_FALSE)) @@ -67,8 +69,9 @@ extern int LASIpGetIp(); #endif -static int -LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop); +static int colonhex_ipv6(char *ipstr, char *netmaskstr, PRIPv6Addr *ipv6, int *netmask); +static int LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop); +static int LASIpAddPatternIPv6(NSErr_t *errp, int netmask, PRIPv6Addr *ipv6, LASIpTree_t **treetop); /* dotdecimal * Takes netmask and ip strings and returns the numeric values, @@ -259,68 +262,108 @@ LASIpTreeDealloc(LASIpTree_t *startnode) * ret code The usual LAS return codes. */ static int -LASIpBuild(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, LASIpTree_t **treetop) +LASIpBuild(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, LASIpContext_t *context) { unsigned int delimiter; /* length of valid token */ char token[64], token2[64]; /* a single ip[+netmask] */ char *curptr; /* current place in attr_pattern */ - int netmask, ip; + int netmask = 0; + int ip = 0; char *plusptr; - int retcode; + int retcode; - if (NULL == treetop) { + if (NULL == context) { return ACL_RES_ERROR; } - /* ip address can be delimited by space, tab, comma, or carriage return - * only. + /* + * IP address can be delimited by space, tab, comma, or carriage return only. */ curptr = attr_pattern; do { delimiter = strcspn(curptr, ", \t"); delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); strncpy(token, curptr, delimiter); - if (delimiter >= sizeof(token)) { + if (delimiter >= sizeof(token)) { return LAS_EVAL_INVALID; - } + } token[delimiter] = '\0'; /* skip all the white space after the token */ - curptr = strpbrk((curptr+delimiter), "1234567890+.*"); - - /* Is there a netmask? */ - plusptr = strchr(token, '+'); - if (plusptr == NULL) { - if (curptr && (*curptr == '+')) { - /* There was a space before (and possibly after) the plus sign*/ - curptr = strpbrk((++curptr), "1234567890.*"); - delimiter = strcspn(curptr, ", \t"); - delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); - if (delimiter >= sizeof(token2)) { - return LAS_EVAL_INVALID; - } - strncpy(token2, curptr, delimiter); - token2[delimiter] = '\0'; - retcode = dotdecimal(token, token2, &ip, &netmask); + curptr = strpbrk((curptr+delimiter), "1234567890+.*ABCDEFabcdef:/"); + + /* + * IPv4 addresses do not have ":" + */ + if( strstr(token,":") == NULL ){ + /* Is there a netmask? */ + plusptr = strchr(token, '+'); + if (plusptr == NULL) { + if (curptr && (*curptr == '+')) { + /* There was a space before (and possibly after) the plus sign*/ + curptr = strpbrk((++curptr), "1234567890.*"); + delimiter = strcspn(curptr, ", \t"); + delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); + if (delimiter >= sizeof(token2)) { + return LAS_EVAL_INVALID; + } + strncpy(token2, curptr, delimiter); + token2[delimiter] = '\0'; + retcode = dotdecimal(token, token2, &ip, &netmask); + if (retcode) + return (retcode); + curptr = strpbrk((++curptr), "1234567890+.*"); + } else { + retcode = dotdecimal(token, "255.255.255.255", &ip, &netmask); + if (retcode) + return (retcode); + } + } else { + /* token is the IP addr string in both cases */ + *plusptr ='\0'; /* truncate the string */ + retcode =dotdecimal(token, ++plusptr, &ip, &netmask); if (retcode) return (retcode); - curptr = strpbrk((++curptr), "1234567890+.*"); + } + + if (LASIpAddPattern(errp, netmask, ip, &context->treetop) != 0) + return LAS_EVAL_INVALID; + } else { + /* + * IPv6 + */ + PRIPv6Addr ipv6; + + plusptr = strchr(token, '/'); + if (plusptr == NULL) { + if (curptr && (*curptr == '/')) { + /* There was a space before (and possibly after) the plus sign */ + curptr = strpbrk((++curptr), "1234567890.*:ABCDEFabcdef"); + delimiter = strcspn(curptr, ", \t"); + delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); + strncpy(token2, curptr, delimiter); + token2[delimiter] = '\0'; + retcode = colonhex_ipv6(token, token2, &ipv6, &netmask); + if (retcode) + return (retcode); + curptr = strpbrk((++curptr), "1234567890+.:ABCDEFabcdef*"); + } else { + retcode = colonhex_ipv6(token, "128", &ipv6, &netmask); + if (retcode) + return (retcode); + } } else { - retcode =dotdecimal(token, "255.255.255.255", &ip, &netmask); + /* token is the IP addr string in both cases */ + *plusptr ='\0'; /* truncate the string */ + retcode = colonhex_ipv6(token, ++plusptr, &ipv6, &netmask); if (retcode) return (retcode); } - } else { - /* token is the IP addr string in both cases */ - *plusptr ='\0'; /* truncate the string */ - retcode =dotdecimal(token, ++plusptr, &ip, &netmask); - if (retcode) - return (retcode); - } - - if (LASIpAddPattern(errp, netmask, ip, treetop) != 0) - return LAS_EVAL_INVALID; + if (LASIpAddPatternIPv6(errp, netmask, &ipv6, &context->treetop_ipv6) != (int)NULL) { + return LAS_EVAL_INVALID; + } + } } while ((curptr != NULL) && (delimiter != 0)); return 0; @@ -361,13 +404,15 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop) if (*treetop == (LASIpTree_t *)NULL) { /* No tree at all */ curptr = LASIpTreeAllocNode(errp); if (curptr == NULL) { - nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_)); + nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1, + XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_)); return ACL_RES_ERROR; } *treetop = curptr; } - /* Special case if the netmask is 0. + /* + * Special case if the netmask is 0. */ if (stopbit > 31) { (*treetop)->action[0] = (LASIpTree_t *)LAS_EVAL_TRUE; @@ -375,24 +420,18 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop) return 0; } - /* follow the tree down the pattern path bit by bit until the * end of the tree is reached (i.e. a constant). */ for (curbit=31,curptr=*treetop; curbit >= 0; curbit--) { - /* Is the current bit ON? If so set curval to 1 else 0 */ curval = (pattern & (1<action[curval]); - curptr->action[curval] = - (LASIpTree_t *)LAS_EVAL_TRUE; - - /* This is the normal exit point. Most other - * exits must be due to errors. - */ + curptr->action[curval] = (LASIpTree_t *)LAS_EVAL_TRUE; + /* This is the normal exit point. Most other exits must be due to errors. */ return 0; } @@ -401,7 +440,8 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop) newptr = LASIpTreeAllocNode(errp); if (newptr == NULL) { LASIpTreeDealloc(*treetop); - nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1)); + nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1, + XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1)); return ACL_RES_ERROR; } curptr->action[curval] = newptr; @@ -451,51 +491,57 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth) { - int bit; - int value; - IPAddr_t ip; - int retcode; - LASIpTree_t *node; - LASIpContext_t *context = NULL; - int rv; - char ip_str[124]; + LASIpContext_t *context = NULL; + LASIpTree_t *node = NULL; + IPAddr_t ip; + PRNetAddr *client_addr = NULL; + struct in_addr client_inaddr; + char ip_str[124]; + int retcode; + int value; + int bit; + int rc = LAS_EVAL_INVALID; + int rv; *cachable = ACL_INDEF_CACHABLE; if (strcmp(attr_name, "ip") != 0) { - nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2, XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name); + nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2, + XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name); return LAS_EVAL_INVALID; } if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { - nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator)); + nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2, + XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator)); return LAS_EVAL_INVALID; } - /* GET THE IP ADDR FROM THE SESSION CONTEXT AND STORE IT IN THE - * VARIABLE ip. + /* + * Get the IP addr from the session context, and store it in "client_addr */ #ifndef UTEST - rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&ip, - subject, resource, auth_info, global_auth); + rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&client_addr, subject, resource, auth_info, global_auth); if (rv != LAS_EVAL_TRUE) { if (subject || resource) { /* Don't ereport if called from ACL_CachableAclList */ - char rv_str[16]; - sprintf(rv_str, "%d", rv); - nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str); + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2, + XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str); } - return LAS_EVAL_FAIL; + return LAS_EVAL_FAIL; } #else ip = (IPAddr_t)LASIpGetIp(); #endif - /* If this is the first time through, build the pattern tree first. + /* + * If this is the first time through, build the pattern tree first. */ if (*LAS_cookie == NULL) { - if (strcspn(attr_pattern, "0123456789.*,+ \t")) { + if (strcspn(attr_pattern, "0123456789.*,+ \tABCDEFabcdef:/")) { return LAS_EVAL_INVALID; } ACL_CritEnter(); @@ -503,13 +549,14 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, *LAS_cookie = context = (LASIpContext_t *)PERM_MALLOC(sizeof(LASIpContext_t)); if (context == NULL) { - nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1, XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_)); + nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1, + XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_)); ACL_CritExit(); return LAS_EVAL_FAIL; } context->treetop = NULL; - retcode = LASIpBuild(errp, attr_name, comparator, attr_pattern, - &context->treetop); + context->treetop_ipv6 = NULL; + retcode = LASIpBuild(errp, attr_name, comparator, attr_pattern, context); if (retcode) { ACL_CritExit(); return (retcode); @@ -523,30 +570,194 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, context = (LASIpContext *) *LAS_cookie; ACL_CritExit(); } + /* + * Check if IP is ipv4/ipv6 + */ + if ( PR_IsNetAddrType( client_addr, PR_IpAddrV4Mapped) || client_addr->raw.family == PR_AF_INET ) { + /* + * IPv4 + */ + + /* Set the appropriate s_addr for ipv4 or ipv4 mapped to ipv6 */ + if (client_addr->raw.family == PR_AF_INET) { + client_inaddr.s_addr = client_addr->inet.ip; + } else { + client_inaddr.s_addr = client_addr->ipv6.ip._S6_un._S6_u32[3]; + } + + node = context->treetop; + ip = (IPAddr_t)PR_ntohl( client_inaddr.s_addr ); + + if(node == NULL){ + rc = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } else { + for (bit = 31; bit >= 0; bit--) { + value = (ip & (IPAddr_t) (1 << bit)) ? 1 : 0; + if (LAS_IP_IS_CONSTANT(node->action[value])){ + /* Reached a result, so return it */ + if (comparator == CMP_OP_EQ){ + rc = (int)(PRSize)node->action[value]; + break; + } else { + rc = ((int)(PRSize)node->action[value] == LAS_EVAL_TRUE) ? LAS_EVAL_FALSE : LAS_EVAL_TRUE; + break; + } + } else { + /* Move on to the next bit */ + node = node->action[value]; + } + } + } + if(rc == LAS_EVAL_INVALID){ + sprintf(ip_str, "%x", (unsigned int)ip); + nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2, + XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str); + } + } else { + /* + * IPv6 + */ + PRIPv6Addr *ipv6 = &(client_addr->ipv6.ip); + LASIpTree_t *node; + int bit_position = 15; + int field = 0; + int addr = 0; + int value; + + node = context->treetop_ipv6; + if ( node == NULL ) { + retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } else { + addr = PR_ntohs( ipv6->_S6_un._S6_u16[field]); + for (bit = 127; bit >= 0 ; bit--, bit_position--) { + value = (addr & (1 << bit_position)) ? 1 : 0; + if (LAS_IP_IS_CONSTANT(node->action[value])) { + /* Reached a result, so return it */ + if (comparator == CMP_OP_EQ){ + return(int)(long)node->action[value]; + } else { + return(((int)(long)node->action[value] == LAS_EVAL_TRUE) ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + } else { + node = node->action[value]; + if ( bit % 16 == 0) { + /* Ok, move to the next field in the IPv6 addr: f:f:next:f:f:f:f:f */ + field++; + addr = PR_ntohs( ipv6->_S6_un._S6_u16[field]); + bit_position = 15; + } + } + } + rc = LAS_EVAL_INVALID; + } + } + return rc; +} + +/* + * The ipv6 version of LASIpAddPattern + */ +static int +LASIpAddPatternIPv6(NSErr_t *errp, int netmask, PRIPv6Addr *ipv6, LASIpTree_t **treetop) +{ + LASIpTree_t *curptr; + LASIpTree_t *newptr; + int stopbit; + int curbit; + int curval; + int field = 0; /* (8) 16 bit fields in a IPv6 address: x:x:x:x:x:x:x:x */ + int addr = 0; + int curbit_position = 15; /* 16 bits: 0-15 */ + + /* stop at the first 1 in the netmask from low to high */ + stopbit = 128 - netmask; - node = context->treetop; - - for (bit=31; bit >=0; bit--) { - value = (ip & (IPAddr_t) (1<action[value])) { - /* Reached a result, so return it */ - if (comparator == CMP_OP_EQ) - return((int)(PRSize)node->action[value]); - else - return(((int)(PRSize)node->action[value] == - LAS_EVAL_TRUE) ? - LAS_EVAL_FALSE : LAS_EVAL_TRUE); - - } else - /* Move on to the next bit */ - node = node->action[value]; + /* Special case if there's no tree. Allocate the first node */ + if (*treetop == (LASIpTree_t *)NULL) { /* No tree at all */ + curptr = LASIpTreeAllocNode(errp); + if (curptr == NULL) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1, + XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_)); + return ACL_RES_ERROR; + } + *treetop = curptr; + } + + addr = PR_ntohs(ipv6->_S6_un._S6_u16[field]); + for (curbit = 127, curptr = *treetop; curbit >= 0; curbit--, curbit_position--){ + /* Is the current bit ON? If so set curval to 1 else 0 */ + curval = (addr & (1 << curbit_position)) ? 1 : 0; + + /* Are we done, if so remove the rest of the tree */ + if (curbit == stopbit) { + LASIpTreeDealloc(curptr->action[curval]); + curptr->action[curval] = (LASIpTree_t *)LAS_EVAL_TRUE; + /* This is the normal exit point. Most other exits must be due to errors. */ + return 0; + } + + /* Oops reached the end - must allocate */ + if (LAS_IP_IS_CONSTANT(curptr->action[curval])) { + newptr = LASIpTreeAllocNode(errp); + if (newptr == NULL) { + LASIpTreeDealloc(*treetop); + nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1, + XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1)); + return ACL_RES_ERROR; + } + curptr->action[curval] = newptr; + } + + /* Keep going down the tree */ + curptr = curptr->action[curval]; + + if ( curbit % 16 == 0) { + /* Ok, move to the next field in the addr */ + field++; + addr = PR_ntohs(ipv6->_S6_un._S6_u16[field]); + curbit_position = 15; + } } + return ACL_RES_ERROR; +} - /* Cannot reach here. Even a 32 bit mismatch has a conclusion in - * the pattern tree. +/* + * This is very similar to dotdecimal(), but for ipv6 addresses + */ +static int +colonhex_ipv6(char *ipstr, char *netmaskstr, PRIPv6Addr *ipv6, int *netmask) +{ + PRNetAddr addr; + /* + * Validate netmaskstr - can only be digits + */ + if (strcspn(netmaskstr, "0123456789")){ + return LAS_EVAL_INVALID; + } + /* + * Validate ipstr - can only have digits, colons, hex chars, and dots + */ + if(strcspn(ipstr, "0123456789:ABCDEFabcdef.")){ + return LAS_EVAL_INVALID; + } + /* + * validate the netmask - must be between 1 and 128 */ - sprintf(ip_str, "%x", (unsigned int)ip); - nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str); - return LAS_EVAL_INVALID; + *netmask = atoi(netmaskstr); + if(*netmask < 1 || *netmask > 128){ + return LAS_EVAL_INVALID; + } + /* + * Get the net addr + */ + if (PR_StringToNetAddr(ipstr, &addr) != PR_SUCCESS){ + return LAS_EVAL_INVALID; + } + /* + * Set the ipv6 addr + */ + *ipv6 = addr.ipv6.ip; + + return 0; } diff --git a/lib/libaccess/lasip.h b/lib/libaccess/lasip.h index c353d9d..c1fe0fc 100644 --- a/lib/libaccess/lasip.h +++ b/lib/libaccess/lasip.h @@ -46,5 +46,6 @@ typedef struct LASIpTree { } LASIpTree_t; typedef struct LASIpContext { - LASIpTree_t *treetop; /* Top of the pattern tree */ + LASIpTree_t *treetop; /* Top of the pattern tree */ + LASIpTree_t *treetop_ipv6; /* Top of the IPv6 pattern tree */ } LASIpContext_t; -- 2.4.3