summaryrefslogtreecommitdiffstats
path: root/lib/libaccess/aclcache.cpp
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /lib/libaccess/aclcache.cpp
downloadds-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.cpp579
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();
+}