diff options
Diffstat (limited to 'lib/libaccess/lasip.cpp')
-rw-r--r-- | lib/libaccess/lasip.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/lib/libaccess/lasip.cpp b/lib/libaccess/lasip.cpp new file mode 100644 index 00000000..2c70a22b --- /dev/null +++ b/lib/libaccess/lasip.cpp @@ -0,0 +1,487 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* aclip.c + * This file contains the IP LAS code. + */ + +#include <stdio.h> +#include <string.h> +#include <netsite.h> +#include <base/plist.h> +#include <libaccess/nserror.h> +#include <libaccess/nsauth.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "lasip.h" +#include "aclutil.h" +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define LAS_IP_IS_CONSTANT(x) (((x) == (LASIpTree_t *)LAS_EVAL_TRUE) || ((x) == (LASIpTree_t *)LAS_EVAL_FALSE)) + +#ifdef UTEST +extern int LASIpGetIp(); +#endif + +static int +LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop); + +/* dotdecimal + * Takes netmask and ip strings and returns the numeric values, + * accounting for wildards in the ip specification. Wildcards in the + * ip override the netmask where they conflict. + * INPUT + * ipstr e.g. "123.45.67.89" + * netmaskstr e.g. "255.255.255.0" + * RETURNS + * *ip + * *netmask e.g. 0xffffff00 + * result NULL on success or else one of the LAS_EVAL_* codes. + */ +int +dotdecimal(char *ipstr, char *netmaskstr, int *ip, int *netmask) +{ + int i; + char token[64]; + char *dotptr; /* location of the "." */ + int dotidx; /* index of the period char */ + + /* Sanity check the patterns */ + + /* Netmask can only have digits and periods. */ + if (strcspn(netmaskstr, "0123456789.")) + return LAS_EVAL_INVALID; + + /* IP can only have digits, periods and "*" */ + if (strcspn(ipstr, "0123456789.*")) + return LAS_EVAL_INVALID; + + *netmask = *ip = 0; /* Start with "don't care" */ + + for (i=0; i<4; i++) { + dotptr = strchr(netmaskstr, '.'); + + /* copy out the token, then point beyond it */ + if (dotptr == NULL) + strcpy(token, netmaskstr); + else { + dotidx = dotptr-netmaskstr; + strncpy(token, netmaskstr, dotidx); + token[dotidx] = '\0'; + netmaskstr = ++dotptr; /* skip the period */ + } + + /* Turn into a number and shift left as appropriate */ + *netmask += (atoi(token))<<(8*(4-i-1)); + + if (dotptr == NULL) + break; + } + + for (i=0; i<4; i++) { + dotptr = strchr(ipstr, '.'); + + /* copy out the token, then point beyond it */ + if (dotptr == NULL) + strcpy(token, ipstr); + else { + dotidx = dotptr-ipstr; + strncpy(token, ipstr, dotidx); + token[dotidx] = '\0'; + ipstr = ++dotptr; + } + + /* check for wildcard */ + if (strcmp(token, "*") == 0) { + switch(i) { + case 0: + if (dotptr == NULL) + *netmask &= 0x00000000; + else + *netmask &= 0x00ffffff; + break; + case 1: + if (dotptr == NULL) + *netmask &= 0xff000000; + else + *netmask &= 0xff00ffff; + break; + case 2: + if (dotptr == NULL) + *netmask &= 0xffff0000; + else + *netmask &= 0xffff00ff; + break; + case 3: + *netmask &= 0xffffff00; + break; + } + continue; + } else { + /* Turn into a number and shift left as appropriate */ + *ip += (atoi(token))<<(8*(4-i-1)); + } + + /* check for end of string */ + if (dotptr == NULL) { + switch(i) { + case 0: + *netmask &= 0xff000000; + break; + case 1: + *netmask &= 0xffff0000; + break; + case 2: + *netmask &= 0xffffff00; + break; + } + break; + } + } + + return (int)NULL; +} + + +/* LASIpTreeAlloc + * Malloc a node and set the actions to LAS_EVAL_FALSE + */ +static LASIpTree_t * +LASIpTreeAllocNode(NSErr_t *errp) +{ + LASIpTree_t *newnode; + + newnode = (LASIpTree_t *)PERM_MALLOC(sizeof(LASIpTree_t)); + if (newnode == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR5000, ACL_Program, 1, XP_GetAdminStr(DBT_lasiptreeallocNoMemoryN_)); + return NULL; + } + newnode->action[0] = (LASIpTree_t *)LAS_EVAL_FALSE; + newnode->action[1] = (LASIpTree_t *)LAS_EVAL_FALSE; + return newnode; +} + + +/* LASIpTreeDealloc + * Deallocates a Tree starting from a given node down. + * INPUT + * startnode Starting node to remove. Could be a constant in + * which case, just return success. + * OUTPUT + * N/A + */ +static void +LASIpTreeDealloc(LASIpTree_t *startnode) +{ + int i; + + if (startnode == NULL) + return; + + /* If this is just a constant then we're done */ + if (LAS_IP_IS_CONSTANT(startnode)) + return; + + /* Else recursively call ourself for each branch down */ + for (i=0; i<2; i++) { + if (!(LAS_IP_IS_CONSTANT(startnode->action[i]))) + LASIpTreeDealloc(startnode->action[i]); + } + + /* Now deallocate the local node */ + PERM_FREE(startnode); +} + +/* + * LASIpBuild + * INPUT + * attr_name The string "ip" - in lower case. + * comparator CmpOpEQ or CmpOpNE only + * attr_pattern A comma-separated list of IP addresses and netmasks + * in dotted-decimal form. Netmasks are optionally + * prepended to the IP address using a plus sign. E.g. + * 255.255.255.0+123.45.67.89. Any byte in the IP address + * (but not the netmask) can be wildcarded using "*" + * context A pointer to the IP LAS context structure. + * RETURNS + * 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) +{ + 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; + char *plusptr; + int retcode; + + /* 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); + 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); + 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); + } + + if (LASIpAddPattern(errp, netmask, ip, treetop) != (int)NULL) + return LAS_EVAL_INVALID; + + } while ((curptr != NULL) && (delimiter != (int)NULL)); + + return (int)NULL; +} + + +/* LASIpAddPattern + * Takes a netmask and IP address and a pointer to an existing IP + * tree and adds nodes as appropriate to recognize the new pattern. + * INPUT + * netmask e.g. 0xffffff00 + * pattern IP address in 4 bytes + * *treetop An existing IP tree or 0 if a new tree + * RETURNS + * ret code NULL on success, ACL_RES_ERROR on failure + * **treetop If this is a new tree, the head of the tree. + */ +static int +LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop) +{ + int stopbit; /* Don't care after this point */ + int curbit; /* current bit we're working on */ + int curval; /* value of pattern[curbit] */ + LASIpTree_t *curptr; /* pointer to the current node */ + LASIpTree_t *newptr; + + /* stop at the first 1 in the netmask from low to high */ + for (stopbit=0; stopbit<32; stopbit++) { + if ((netmask&(1<<stopbit)) != 0) + break; + } + + /* 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; + } + + /* Special case if the netmask is 0. + */ + if (stopbit > 31) { + curptr->action[0] = (LASIpTree_t *)LAS_EVAL_TRUE; + curptr->action[1] = (LASIpTree_t *)LAS_EVAL_TRUE; + 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<<curbit)) ? 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]; + } + + return ACL_RES_ERROR; +} + +/* LASIpFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASIpFlush(void **las_cookie) +{ + if (*las_cookie == NULL) + return; + + LASIpTreeDealloc(((LASIpContext_t *)*las_cookie)->treetop); + PERM_FREE(*las_cookie); + *las_cookie = NULL; + return; +} + +/* + * LASIpEval + * INPUT + * attr_name The string "ip" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of IP addresses and netmasks + * in dotted-decimal form. Netmasks are optionally + * prepended to the IP address using a plus sign. E.g. + * 255.255.255.0+123.45.67.89. Any byte in the IP address + * (but not the netmask) can be wildcarded using "*" + * *cachable Always set to ACL_INDEF_CACHABLE + * subject Subject property list + * resource Resource property list + * auth_info The authentication info if any + * RETURNS + * ret code The usual LAS return codes. + */ +int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, + 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; + int rv; + char ip_str[124]; + + *cachable = ACL_INDEF_CACHABLE; + + if (strcmp(attr_name, "ip") != 0) { + 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)); + return LAS_EVAL_INVALID; + } + + /* GET THE IP ADDR FROM THE SESSION CONTEXT AND STORE IT IN THE + * VARIABLE ip. + */ +#ifndef UTEST + rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&ip, + 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); + } + return LAS_EVAL_FAIL; + } +#else + ip = (IPAddr_t)LASIpGetIp(); +#endif + + /* If this is the first time through, build the pattern tree first. + */ + if (*LAS_cookie == NULL) { + if (strcspn(attr_pattern, "0123456789.*,+ \t")) { + return LAS_EVAL_INVALID; + } + ACL_CritEnter(); + if (*LAS_cookie == NULL) { /* must check again */ + *LAS_cookie = context = + (LASIpContext_t *)PERM_MALLOC(sizeof(LASIpContext_t)); + if (context == NULL) { + 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); + if (retcode) { + ACL_CritExit(); + return (retcode); + } + } + ACL_CritExit(); + } else + context = (LASIpContext *) *LAS_cookie; + + node = context->treetop; + + 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) + 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]; + } + + /* Cannot reach here. Even a 32 bit mismatch has a conclusion in + * the pattern tree. + */ + sprintf(ip_str, "%x", ip); + nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str); + return LAS_EVAL_INVALID; +} + |