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/usrcache.cpp | |
download | ds-ldapserver7x.tar.gz ds-ldapserver7x.tar.xz ds-ldapserver7x.zip |
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'lib/libaccess/usrcache.cpp')
-rw-r--r-- | lib/libaccess/usrcache.cpp | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/lib/libaccess/usrcache.cpp b/lib/libaccess/usrcache.cpp new file mode 100644 index 00000000..2f79d2cc --- /dev/null +++ b/lib/libaccess/usrcache.cpp @@ -0,0 +1,657 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* #define DBG_PRINT */ + + +#include <netsite.h> +extern "C" { +#include <secitem.h> +} +#include <base/crit.h> +#include <ldaputil/errors.h> +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/authdb.h> +#include "permhash.h" + +/* uid is unique within a database. The user cache tables are stored per + * database. The following table maps a database name to the corresponding + * user cache table. The user cache table is another hash table which stores + * the UserCacheObj instances. + */ +static PRHashTable *databaseUserCacheTable = 0; +static time_t acl_usr_cache_lifetime = (time_t)120; +static PRCList *usrobj_list = 0; +static const int num_usrobj = 200; +static CRITICAL usr_hash_crit = NULL; /* Controls user cache hash tables & */ + /* usrobj link list */ +static pool_handle_t *usrcache_pool = NULL; +static PRHashTable *singleDbTable = 0; + +#define USEROBJ_PTR(l) \ + ((UserCacheObj*) ((char*) (l) - offsetof(UserCacheObj, list))) + +static void user_hash_crit_enter (void) +{ + /* Caching may be disabled (usr_hash_crit will be NULL) */ + if (usr_hash_crit) crit_enter(usr_hash_crit); +} + +static void user_hash_crit_exit (void) +{ + /* Caching may be disabled (usr_hash_crit will be NULL) */ + if (usr_hash_crit) crit_exit(usr_hash_crit); +} + +static void user_hash_crit_init (void) +{ + usr_hash_crit = crit_init(); +} + +static PRHashNumber +usr_cache_hash_cert(const void *key) +{ + PRHashNumber h; + const unsigned char *s; + unsigned int i = 0; + SECItem *derCert = (SECItem *)key; + unsigned int len = derCert->len; + + h = 0; + for (s = (const unsigned char *)derCert->data; i < len; s++, i++) + h = (h >> 28) ^ (h << 4) ^ *s; + return h; +} + +static PRHashNumber +usr_cache_hash_fn (const void *key) +{ + UserCacheObj *usrObj = (UserCacheObj *)key; + + if (usrObj->derCert) + return usr_cache_hash_cert(usrObj->derCert); + else + return PR_HashCaseString(usrObj->uid); +} + +static int +usr_cache_compare_certs(const void *v1, const void *v2) +{ + const SECItem *c1 = (const SECItem *)v1; + const SECItem *c2 = (const SECItem *)v2; + + return (c1->len == c2 ->len && !memcmp(c1->data, c2->data, c1->len)); +} + +static int +usr_cache_compare_fn(const void *v1, const void *v2) +{ + UserCacheObj *usrObj1 = (UserCacheObj *)v1; + UserCacheObj *usrObj2 = (UserCacheObj *)v2; + + if (usrObj1->derCert && usrObj2->derCert) + return usr_cache_compare_certs(usrObj1->derCert, usrObj2->derCert); + else if (!usrObj1->derCert && !usrObj2->derCert) + return PR_CompareCaseStrings(usrObj1->uid, usrObj1->uid); + else + return 0; +} + +static PRHashTable *alloc_db2uid_table () +{ + return PR_NewHashTable(0, + usr_cache_hash_fn, + usr_cache_compare_fn, + PR_CompareValues, + &ACLPermAllocOps, + usrcache_pool); +} + + +int acl_usr_cache_set_timeout (const int nsec) +{ + acl_usr_cache_lifetime = (time_t)nsec; + return 0; +} + + +int acl_usr_cache_enabled () +{ + return (acl_usr_cache_lifetime > 0); +} + + +int acl_usr_cache_init () +{ + UserCacheObj *usrobj; + int i; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + DBG_PRINT1("usrcache is disabled"); + return 0; + } + + usrcache_pool = pool_create(); + user_hash_crit_init(); + + if (acl_num_databases() == 0) { + /* Something wrong -- No databases registered yet! */ + return -1; + } + else if (acl_num_databases() == 1) { + /* Optimize for single database */ + DBG_PRINT1("Optimizing usrcache for single db"); + singleDbTable = alloc_db2uid_table(); + } + else { + singleDbTable = 0; + databaseUserCacheTable = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + usrcache_pool); + } + + /* Allocate first UserCacheObj and initialize the circular link list */ + usrobj = (UserCacheObj *)pool_malloc(usrcache_pool, sizeof(UserCacheObj)); + if (!usrobj) return -1; + memset((void *)usrobj, 0, sizeof(UserCacheObj)); + usrobj_list = &usrobj->list; + PR_INIT_CLIST(usrobj_list); + + /* Allocate rest of the UserCacheObj and put them in the link list */ + for(i = 0; i < num_usrobj; i++){ + usrobj = (UserCacheObj *)pool_malloc(usrcache_pool, + sizeof(UserCacheObj)); + + if (!usrobj) return -1; + memset((void *)usrobj, 0, sizeof(UserCacheObj)); + PR_INSERT_AFTER(&usrobj->list, usrobj_list); + } + + return (singleDbTable || databaseUserCacheTable) ? 0 : -1; +} + +/* If the user hash table exists in the databaseUserCacheTable then return it. + * Otherwise, create a new hash table, insert it in the databaseUserCacheTable + * and then return it. + */ +static int usr_cache_table_get (const char *dbname, PRHashTable **usrTable) +{ + PRHashTable *table; + + if (singleDbTable) { + *usrTable = singleDbTable; + return LAS_EVAL_TRUE; + } + + user_hash_crit_enter(); + + table = (PRHashTable *)PR_HashTableLookup(databaseUserCacheTable, + dbname); + + if (!table) { + /* create a new table and insert it in the databaseUserCacheTable */ + table = alloc_db2uid_table(); + + if (table) { + PR_HashTableAdd(databaseUserCacheTable, + pool_strdup(usrcache_pool, dbname), + table); + } + } + + *usrTable = table; + + user_hash_crit_exit(); + + return table ? LAS_EVAL_TRUE : LAS_EVAL_FAIL; +} + +int acl_usr_cache_insert (const char *uid, const char *dbname, + const char *userdn, const char *passwd, + const char *group, + const SECItem *derCert, const time_t time) +{ + PRHashTable *usrTable; + UserCacheObj *usrobj; + UserCacheObj key; + int rv; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + return LAS_EVAL_TRUE; + } + + rv = usr_cache_table_get (dbname, &usrTable); + + if (rv != LAS_EVAL_TRUE) return rv; + + user_hash_crit_enter(); + + key.uid = (char *)uid; + key.derCert = (SECItem *)derCert; + + usrobj = (UserCacheObj *)PR_HashTableLookup(usrTable, &key); + + if (usrobj) { + time_t elapsed = time - usrobj->time; + int expired = (elapsed >= acl_usr_cache_lifetime); + + /* Free & reset the old values in usrobj if -- there is an old value + * and if the new value is given then it is different or the usrobj + * has expired */ + /* Set the field if the new value is given and the field is not set */ + /* If the usrobj has not expired then we only want to update the field + * whose new value is non-NULL and different */ + + /* Work on the 'uid' field */ + if (usrobj->uid && + (uid ? strcmp(usrobj->uid, uid) : expired)) + { + pool_free(usrcache_pool, usrobj->uid); + usrobj->uid = 0; + } + if (uid && !usrobj->uid) { + usrobj->uid = pool_strdup(usrcache_pool, uid); + } + + /* Work on the 'userdn' field */ + if (usrobj->userdn && + (userdn ? strcmp(usrobj->userdn, userdn) : expired)) + { + pool_free(usrcache_pool, usrobj->userdn); + usrobj->userdn = 0; + } + if (userdn && !usrobj->userdn) { + usrobj->userdn = pool_strdup(usrcache_pool, userdn); + } + + /* Work on the 'passwd' field */ + if (usrobj->passwd && + (passwd ? strcmp(usrobj->passwd, passwd) : expired)) + { + pool_free(usrcache_pool, usrobj->passwd); + usrobj->passwd = 0; + } + if (passwd && !usrobj->passwd) { + usrobj->passwd = pool_strdup(usrcache_pool, passwd); + } + + /* Work on the 'group' field -- not replace a valid group */ + if (!expired && usrobj->group && + (group ? strcmp(usrobj->group, group) : expired)) + { + pool_free(usrcache_pool, usrobj->group); + usrobj->group = 0; + } + if (group && !usrobj->group) { + usrobj->group = pool_strdup(usrcache_pool, group); + } + + /* Work on the 'derCert' field */ + if (usrobj->derCert && + (derCert ? (derCert->len != usrobj->derCert->len || + memcmp(usrobj->derCert->data, derCert->data, + derCert->len)) + : expired)) + { + SECITEM_FreeItem(usrobj->derCert, PR_TRUE); + usrobj->derCert = 0; + } + if (derCert && !usrobj->derCert) { + usrobj->derCert = SECITEM_DupItem((SECItem *)derCert); + } + + /* Reset the time only if the usrobj has expired */ + if (expired) { + DBG_PRINT1("Replace "); + usrobj->time = time; + } + else { + DBG_PRINT1("Update "); + } + } + else { + /* Get the last usrobj from the link list, erase it and use it */ + /* Maybe the last usrobj is not invalid yet but we don't want to grow + * the list of usrobjs. The last obj is the best candidate for being + * not valid. We don't want to compare the time -- just use it. + */ + PRCList *tail = PR_LIST_TAIL(usrobj_list); + usrobj = USEROBJ_PTR(tail); + + /* If the removed usrobj is in the hashtable, remove it from there */ + if (usrobj->hashtable) { + PR_HashTableRemove(usrobj->hashtable, usrobj); + } + + /* Free the memory associated with the usrobj */ + if (usrobj->userdn) pool_free(usrcache_pool, usrobj->userdn); + if (usrobj->passwd) pool_free(usrcache_pool, usrobj->passwd); + if (usrobj->group) pool_free(usrcache_pool, usrobj->group); + if (usrobj->derCert) SECITEM_FreeItem(usrobj->derCert, PR_TRUE); + if (usrobj->uid) pool_free(usrcache_pool, usrobj->uid); + + /* Fill in the usrobj with the current data */ + usrobj->uid = pool_strdup(usrcache_pool, uid); + usrobj->userdn = userdn ? pool_strdup(usrcache_pool, userdn) : 0; + usrobj->passwd = passwd ? pool_strdup(usrcache_pool, passwd) : 0; + usrobj->derCert = derCert ? SECITEM_DupItem((SECItem *)derCert) : 0; + usrobj->group = group ? pool_strdup(usrcache_pool, group) : 0; + usrobj->time = time; + + /* Add the usrobj to the user hash table */ + PR_HashTableAdd(usrTable, usrobj, usrobj); + usrobj->hashtable = usrTable; + DBG_PRINT1("Insert "); + } + + /* Move the usrobj to the head of the list */ + PR_REMOVE_LINK(&usrobj->list); + PR_INSERT_AFTER(&usrobj->list, usrobj_list); + + /* Set the time in the UserCacheObj */ + if (usrobj) { + rv = LAS_EVAL_TRUE; + } + else { + rv = LAS_EVAL_FAIL; + } + + DBG_PRINT4("acl_usr_cache_insert: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + uid, time); + + user_hash_crit_exit(); + return rv; +} + +static int acl_usr_cache_get_usrobj (const char *uid, const SECItem *derCert, + const char *dbname, const time_t time, + UserCacheObj **usrobj_out) +{ + PRHashTable *usrtable; + UserCacheObj *usrobj; + UserCacheObj key; + time_t elapsed; + int rv; + + *usrobj_out = 0; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + return LAS_EVAL_FALSE; + } + + rv = usr_cache_table_get(dbname, &usrtable); + if (!usrtable) return LAS_EVAL_FALSE; + + key.uid = (char *)uid; + key.derCert = (SECItem *)derCert; + + usrobj = (UserCacheObj *)PR_HashTableLookup(usrtable, &key); + + if (!usrobj) return LAS_EVAL_FALSE; + + rv = LAS_EVAL_FALSE; + + elapsed = time - usrobj->time; + + /* If the cache is valid, return the usrobj */ + if (elapsed < acl_usr_cache_lifetime) { + rv = LAS_EVAL_TRUE; + *usrobj_out = usrobj; + DBG_PRINT4("usr_cache found: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + usrobj->uid, time); + } + else { + DBG_PRINT4("usr_cache expired: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + usrobj->uid, time); + } + + return rv; +} + +int acl_usr_cache_passwd_check (const char *uid, const char *dbname, + const char *passwd, + const time_t time, char **dn, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->passwd && passwd && + !strcmp(usrobj->passwd, passwd)) + { + /* extract dn from the usrobj */ + *dn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + rv = LAS_EVAL_TRUE; + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_passwd_check: uid = \"%s\" at time = %ld\n", + uid, time); + user_hash_crit_exit(); + + return rv; +} + + +static int group_check_helper (UserCacheObj *usrobj, void *val) +{ + if (usrobj->group && !strcmp(usrobj->group, (char *)val)) + return LAS_EVAL_TRUE; + else + return LAS_EVAL_FALSE; +} + +int acl_usr_cache_group_check (const char *uid, const char *dbname, + const char *group, const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->group && group && + !strcmp(usrobj->group, group)) + { + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_group_check: uid = \"%s\" group = \"%s\"\n", + uid, group ? group : "<NONE>"); + user_hash_crit_exit(); + + return rv; +} + +int acl_usr_cache_group_len_check (const char *uid, const char *dbname, + const char *group, const int len, + const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->group && group && + !strncmp(usrobj->group, group, len)) + { + rv = LAS_EVAL_TRUE; + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_group_check: uid = \"%s\" group = \"%s\"\n", + uid, group); + user_hash_crit_exit(); + + return rv; +} + + +int acl_usr_cache_get_userdn (const char *uid, const char *dbname, + const time_t time, char **userdn, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + *userdn = 0; + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE) { + *userdn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + DBG_PRINT1("Success "); + } + else { + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_get_userdn: uid = \"%s\" userdn = \"%s\"\n", + uid, *userdn ? *userdn : "<NONE>"); + user_hash_crit_exit(); + + return *userdn ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; +} + +int acl_usr_cache_userdn_check (const char *uid, const char *dbname, + const char *userdn, const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->userdn && userdn && + !strcmp(usrobj->userdn, userdn)) + { + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_userdn_check: uid = \"%s\" userdn = \"%s\"\n", + uid, userdn ? userdn : "<NONE>"); + user_hash_crit_exit(); + + return rv; +} + +int acl_usr_cache_set_userdn (const char *uid, const char *dbname, + const char *userdn, const time_t time) +{ + int rv; + + /* acl_usr_cache_insert updates the existing un-expired entry or creates a + * new one */ + rv = acl_usr_cache_insert(uid, dbname, userdn, 0, 0, 0, time); + + return rv; +} + +int acl_usr_cache_get_group (const char *uid, const char *dbname, + const time_t time, char **group, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + *group = 0; + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE) { + *group = usrobj->group ? pool_strdup(pool, usrobj->group) : 0; + DBG_PRINT1("Success "); + } + else { + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_get_group: uid = \"%s\" group = \"%s\"\n", + uid, *group ? *group : "<NONE>"); + user_hash_crit_exit(); + + return *group ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; +} + +int acl_usr_cache_set_group (const char *uid, const char *dbname, + const char *group, const time_t time) +{ + int rv; + + /* acl_usr_cache_insert updates the existing un-expired entry or creates a + * new one */ + rv = acl_usr_cache_insert(uid, dbname, 0, 0, group, 0, time); + + return rv; +} + +int acl_cert_cache_insert (void *cert_in, const char *dbname, + const char *uid, const char *dn, + const time_t time) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + SECItem derCert = cert->derCert; + int rv; + + rv = acl_usr_cache_insert(uid, dbname, dn, 0, 0, &derCert, time); + + return rv; +} + +/* Returns LAS_EVAL_TRUE if the user's cache is valid and returns uid */ +int acl_cert_cache_get_uid (void *cert_in, const char *dbname, + const time_t time, char **uid, char **dn, + pool_handle_t *pool) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + SECItem derCert = cert->derCert; + UserCacheObj *usrobj = 0; + int rv; + + rv = acl_usr_cache_get_usrobj(0, &derCert, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj && usrobj->uid) { + *uid = pool_strdup(pool, usrobj->uid); + *dn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + } + else { + *uid = 0; + *dn = 0; + rv = LAS_EVAL_FALSE; + } + + return rv; +} + |