diff options
author | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
---|---|---|
committer | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
commit | b2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch) | |
tree | cf58939393a9032182c4fbc4441164a9456e82f8 /lib/libaccess/aclcache.cpp | |
download | ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip |
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'lib/libaccess/aclcache.cpp')
-rw-r--r-- | lib/libaccess/aclcache.cpp | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/lib/libaccess/aclcache.cpp b/lib/libaccess/aclcache.cpp new file mode 100644 index 00000000..34d2ecfb --- /dev/null +++ b/lib/libaccess/aclcache.cpp @@ -0,0 +1,579 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <base/nsassert.h> +#include <base/crit.h> +#include <base/ereport.h> +#include <plhash.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/ldapacl.h> +#include "aclutil.h" +#include "permhash.h" +#include "aclcache.h" + + +static CRITICAL acl_hash_crit = NULL; /* Controls Global Hash */ + +enum { + ACL_URI_HASH, + ACL_URI_GET_HASH +}; + +/* ACL_ListHashKeyHash + * Given an ACL List address, computes a randomized hash value of the + * ACL structure pointer addresses by simply adding them up. Returns + * the resultant hash value. + */ +static PLHashNumber +ACL_ListHashKeyHash(const void *Iacllist) +{ + PLHashNumber hash=0; + ACLWrapper_t *wrap; + ACLListHandle_t *acllist=(ACLListHandle_t *)Iacllist; + + for (wrap=acllist->acl_list_head; wrap; wrap=wrap->wrap_next) { + hash += (PLHashNumber)(PRSize)wrap->acl; + } + + return (hash); +} + +/* ACL_ListHashKeyCompare + * Given two acl lists, compares the addresses of the acl pointers within + * them to see if theyre identical. Returns 1 if equal, 0 otherwise. + */ +static int +ACL_ListHashKeyCompare(const void *Iacllist1, const void *Iacllist2) +{ + ACLWrapper_t *wrap1, *wrap2; + ACLListHandle_t *acllist1=(ACLListHandle_t *)Iacllist1; + ACLListHandle_t *acllist2=(ACLListHandle_t *)Iacllist2; + + if (acllist1->acl_count != acllist2->acl_count) + return 0; + + wrap1 = acllist1->acl_list_head; + wrap2 = acllist2->acl_list_head; + + while ((wrap1 != NULL) && (wrap2 != NULL)) { + if (wrap1->acl != wrap2->acl) + return 0; + wrap1 = wrap1->wrap_next; + wrap2 = wrap2->wrap_next; + } + + if ((wrap1 != NULL) || (wrap2 != NULL)) + return 0; + else + return 1; +} + +/* ACL_ListHashValueCompare + * Returns 1 if equal, 0 otherwise + */ +static int +ACL_ListHashValueCompare(const void *acllist1, const void *acllist2) +{ + + return (acllist1 == acllist2); +} + +void +ACL_ListHashInit() +{ + ACLListHash = PR_NewHashTable(200, + ACL_ListHashKeyHash, + ACL_ListHashKeyCompare, + ACL_ListHashValueCompare, + &ACLPermAllocOps, + NULL); + if (ACLListHash == NULL) { + ereport(LOG_SECURITY, "Unable to allocate ACL List Hash\n"); + return; + } + + return; +} + +static void +ACL_ListHashDestroy() +{ + if (ACLListHash) { + PR_HashTableDestroy(ACLListHash); + ACLListHash = NULL; + } + + return; +} + +/* ACL_ListHashUpdate + * Typically called with the &rq->acllist. Checks if the newly generated + * acllist matches one that's already been created. If so, toss the new + * list and set the pointer to the old list in its place. + */ +void +ACL_ListHashUpdate(ACLListHandle_t **acllistp) +{ + NSErr_t *errp = 0; + ACLListHandle_t *tmp_acllist; + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + + tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp); + if (tmp_acllist && tmp_acllist != *acllistp) { + NS_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS); + ACL_ListDestroy(errp, *acllistp); + *acllistp = tmp_acllist; + NS_ASSERT(ACL_CritHeld()); + tmp_acllist->ref_count++; /* we're gonna use it */ + } else { /* Wasn't in the list */ + PR_HashTableAdd(ACLListHash, *acllistp, *acllistp); + } + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +/* ACL_ListCacheEnter + * In some cases, the URI cache is useless. E.g. when virtual servers are used. + * When that happens, the List Cache is still useful, because the cached ACL + * List has the Eval cache in it, plus any LAS caches. + */ +NSAPI_PUBLIC void +ACL_ListHashEnter(ACLListHandle_t **acllistp) +{ + NSErr_t *errp = 0; + + ACL_CritEnter(); + + /* Look for a matching ACL List and use it if we find one. */ + if (*acllistp) { + NS_ASSERT(*acllistp != ACL_LIST_NO_ACLS); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + ACL_ListHashUpdate(acllistp); + } else { + *acllistp = ACL_LIST_NO_ACLS; + } + + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +/* ACL_ListHashCheck + * When Virtual Servers are active, and the ACL URI cache is inactive, someone + * with an old ACL List pointer can check to see if it's still valid. This will + * also increment the reference count on it. + */ +NSAPI_PUBLIC int +ACL_ListHashCheck(ACLListHandle_t **acllistp) +{ + ACLListHandle_t *tmp_acllist; + + if (*acllistp == ACL_LIST_NO_ACLS) return 1; + + ACL_CritEnter(); + + tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp); + if (tmp_acllist) { + NS_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS); + *acllistp = tmp_acllist; + NS_ASSERT(ACL_CritHeld()); + tmp_acllist->ref_count++; /* we're gonna use it */ + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return 1; /* Normal path */ + } else { /* Wasn't in the list */ + ACL_CritExit(); + return 0; + } + +} + + +void +ACL_UriHashDestroy(void) +{ + if (acl_uri_hash) { + PR_HashTableDestroy(acl_uri_hash); + acl_uri_hash = NULL; + } + if (acl_uri_get_hash) { + PR_HashTableDestroy(acl_uri_get_hash); + acl_uri_get_hash = NULL; + } + pool_destroy((void **)acl_uri_hash_pool); + acl_uri_hash_pool = NULL; + +} + +void +ACL_Destroy(void) +{ + ACL_ListHashDestroy(); + ACL_UriHashDestroy(); + ACL_LasHashDestroy(); +} + +/* Only used in ASSERT statements to verify that we have the lock */ +int +ACL_CritHeld(void) +{ + return (crit_owner_is_me(acl_hash_crit)); +} + +NSAPI_PUBLIC void +ACL_CritEnter(void) +{ + crit_enter(acl_hash_crit); +} + +NSAPI_PUBLIC void +ACL_CritExit(void) +{ + crit_exit(acl_hash_crit); +} + +void +ACL_CritInit(void) +{ + acl_hash_crit = crit_init(); +} + +void +ACL_UriHashInit(void) +{ + acl_uri_hash = PR_NewHashTable(200, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + acl_uri_get_hash = PR_NewHashTable(200, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + acl_uri_hash_pool = pool_create(); +} + +/* ACL_CacheCheck + * INPUT + * uri A URI string pointer + * acllistp A pointer to an acllist placeholder. E.g. &rq->acllist + * OUTPUT + * return 1 if cached. 0 if not. The reference count on the ACL List + * is INCREMENTED, and will be decremented when ACL_EvalDestroy or + * ACL_ListDecrement is + * called. + */ +int +ACL_INTCacheCheck(int which, char *uri, ACLListHandle_t **acllistp) +{ + PLHashTable *hash; + NS_ASSERT(uri && acl_uri_hash && acl_uri_get_hash); + + /* ACL cache: If the ACL List is already in the cache, there's no need + * to go through the pathcheck directives. + * NULL means that the URI hasn't been accessed before. + * ACL_LIST_NO_ACLS + * means that the URI has no ACLs. + * Anything else is a pointer to the acllist. + */ + ACL_CritEnter(); + + /* Get the pointer to the hash table after acquiring the lock */ + if (which == ACL_URI_HASH) + hash = acl_uri_hash; + else + hash = acl_uri_get_hash; + + *acllistp = (ACLListHandle_t *)PR_HashTableLookup(hash, uri); + if (*acllistp != NULL) { + if (*acllistp != ACL_LIST_NO_ACLS) { + NS_ASSERT((*acllistp)->ref_count >= 0); + NS_ASSERT(ACL_CritHeld()); + (*acllistp)->ref_count++; + } + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return 1; /* Normal path */ + } + + ACL_CritExit(); + return 0; +} + +int +ACL_CacheCheckGet(char *uri, ACLListHandle_t **acllistp) +{ + return (ACL_INTCacheCheck(ACL_URI_GET_HASH, uri, acllistp)); +} + +int +ACL_CacheCheck(char *uri, ACLListHandle_t **acllistp) +{ + return (ACL_INTCacheCheck(ACL_URI_HASH, uri, acllistp)); +} + + +/* ACL_CacheEnter + * INPUT + * acllist or 0 if there were no ACLs that applied. + * OUTPUT + * The acllist address may be changed if it matches an existing one. + */ +static void +ACL_INTCacheEnter(int which, char *uri, ACLListHandle_t **acllistp) +{ + ACLListHandle_t *tmpacllist; + NSErr_t *errp = 0; + PLHashTable *hash; + + NS_ASSERT(uri); + + ACL_CritEnter(); + + /* Get the pointer to the hash table after acquiring the lock */ + if (which == ACL_URI_HASH) + hash = acl_uri_hash; + else + hash = acl_uri_get_hash; + + /* Check again (now that we're in the critical section) to see if + * someone else created an ACL List for this URI. If so, discard the + * list that we made and replace it with the one just found. + */ + tmpacllist = (ACLListHandle_t *)PR_HashTableLookup(hash, uri); + if (tmpacllist != NULL) { + if (tmpacllist != ACL_LIST_NO_ACLS) { + NS_ASSERT(ACL_CritHeld()); + tmpacllist->ref_count++; /* we're going to use it */ + } + ACL_CritExit(); + if (*acllistp && *acllistp != ACL_LIST_NO_ACLS) { + ACL_ListDestroy(errp, *acllistp); + } + *acllistp = tmpacllist; + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; + } + + /* Didn't find another list, so put ours in. */ + /* Look for a matching ACL List and use it if we find one. */ + if (*acllistp) { + NS_ASSERT(*acllistp != ACL_LIST_NO_ACLS); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + ACL_ListHashUpdate(acllistp); + } else { + *acllistp = ACL_LIST_NO_ACLS; + } + PR_HashTableAdd(hash, pool_strdup((void **)acl_uri_hash_pool, uri), (void *)*acllistp); + + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +void +ACL_CacheEnter(char *uri, ACLListHandle_t **acllistp) +{ + ACL_INTCacheEnter(ACL_URI_HASH, uri, acllistp); + return; +} + +void +ACL_CacheEnterGet(char *uri, ACLListHandle_t **acllistp) +{ + ACL_INTCacheEnter(ACL_URI_GET_HASH, uri, acllistp); + return; +} + +/* ACL_AddAclName + * Adds the ACLs for just the terminal object specified in a pathname. + * INPUT + * path The filesystem pathname of the terminal object. + * acllistp The address of the list of ACLs found thus far. + * Could be NULL. If so, a new acllist will be allocated (if any + * acls are found). Otherwise the existing list will be added to. + * masterlist Usually acl_root_30. + */ +void +ACL_AddAclName(char *path, ACLListHandle_t **acllistp, ACLListHandle_t +*masterlist) +{ + ACLHandle_t *acl; + NSErr_t *errp = 0; + +#ifdef XP_WIN32 + acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_INSENSITIVE); +#else + acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_SENSITIVE); +#endif + if (!acl) + return; + + NS_ASSERT(ACL_AssertAcl(acl)); + + if (!*acllistp) + *acllistp = ACL_ListNew(errp); + ACL_ListAppend(NULL, *acllistp, acl, 0); + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + + +/* ACL_GetPathAcls + * Adds the ACLs for all directories plus the terminal object along a given + * filesystem pathname. For each pathname component, look for the name, the + * name + "/", and the name + "/*". The last one is because the resource + * picker likes to postpend "/*" for directories. + * INPUT + * path The filesystem pathname of the terminal object. + * acllistp The address of the list of ACLs found thus far. + * Could be NULL. If so, a new acllist will be allocated (if any + * acls are found). Otherwise the existing list will be added to. + * prefix A string to be prepended to the path component when looking + * for a matching ACL tag. + */ +void +ACL_GetPathAcls(char *path, ACLListHandle_t **acllistp, char *prefix, +ACLListHandle_t *masterlist) +{ + char *slashp=path; + int slashidx; + char ppath[ACL_PATH_MAX]; + int prefixlen; + char *dst; + + NS_ASSERT(path); + NS_ASSERT(prefix); + + dst = strncpy(ppath, prefix, ACL_PATH_MAX); + if (dst >= (ppath+ACL_PATH_MAX-1)) { + ereport(LOG_SECURITY, "Abort - the path is too long for ACL_GetPathAcls to handle\n"); + abort(); + } + prefixlen = strlen(ppath); + + /* Handle the first "/". i.e. the root directory */ + if (*path == '/') { + ppath[prefixlen]='/'; + ppath[prefixlen+1]='\0'; + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + slashp = path; + } + + do { + slashp = strchr(++slashp, '/'); + if (slashp) { + slashidx = slashp - path; + strncpy(&ppath[prefixlen], path, slashidx); + ppath[slashidx+prefixlen] = '\0'; + ACL_AddAclName(ppath, acllistp, masterlist); + /* Must also handle "/a/b/" in addition to "/a/b" */ + strcat(ppath, "/"); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + continue; + } + strcpy(&ppath[prefixlen], path); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "/"); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + break; + } while (slashp); + +} + + +static int get_is_owner_default (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + /* Make sure we don't generate error "all getters declined" message from + * ACL_GetAttribute. + */ + PListInitProp(subject, ACL_ATTR_IS_OWNER_INDEX, ACL_ATTR_IS_OWNER, + "true", 0); + + return LAS_EVAL_TRUE; +} + + +NSAPI_PUBLIC int +ACL_Init(void) +{ + ACL_InitAttr2Index(); + ACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s)); + oldACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s)); + NS_ASSERT(ACLGlobal && oldACLGlobal); + ACL_DATABASE_POOL = pool_create(); + ACL_METHOD_POOL = pool_create(); + ACL_CritInit(); + ACL_UriHashInit(); + ACL_ListHashInit(); + ACL_LasHashInit(); + ACL_Init2(); + init_ldb_rwlock(); + ACL_RegisterInit(); + + return 0; +} + +/* This one gets called at startup AND at cache flush time. */ +void +ACL_Init2(void) +{ + + /* Register the ACL functions */ + ACL_LasRegister(NULL, "timeofday", LASTimeOfDayEval, LASTimeOfDayFlush); + ACL_LasRegister(NULL, "dayofweek", LASDayOfWeekEval, LASDayOfWeekFlush); + ACL_LasRegister(NULL, "ip", LASIpEval, LASIpFlush); + ACL_LasRegister(NULL, "dns", LASDnsEval, LASDnsFlush); + ACL_LasRegister(NULL, "dnsalias", LASDnsEval, LASDnsFlush); + ACL_LasRegister(NULL, "group", LASGroupEval, (LASFlushFunc_t)NULL); + ACL_LasRegister(NULL, "user", LASUserEval, (LASFlushFunc_t)NULL); +#ifdef MCC_ADMSERV + ACL_LasRegister(NULL, "program", LASProgramEval, (LASFlushFunc_t)NULL); +#endif + + ACL_AttrGetterRegister(NULL, ACL_ATTR_USERDN, + get_userdn_ldap, + ACL_METHOD_ANY, ACL_DBTYPE_ANY, + ACL_AT_END, NULL); + return; +} + +NSAPI_PUBLIC int +ACL_InitPostMagnus(void) +{ + int rv; + + rv = ACL_AttrGetterRegister(NULL, ACL_ATTR_IS_OWNER, + get_is_owner_default, + ACL_METHOD_ANY, ACL_DBTYPE_ANY, + ACL_AT_END, NULL); + return rv; +} + +NSAPI_PUBLIC int +ACL_LateInitPostMagnus(void) +{ + return acl_usr_cache_init(); +} |