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 | |
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')
187 files changed, 60143 insertions, 0 deletions
diff --git a/lib/base/Makefile b/lib/base/Makefile new file mode 100644 index 00000000..f58195d3 --- /dev/null +++ b/lib/base/Makefile @@ -0,0 +1,111 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# The base library contains the base set of functionality for ldapserver +# servers. Cross-platform I/O and other generic functions are +# provided here. + + +MCOM_ROOT=../../.. +MODULE=LibBase + +include ../../nsdefs.mk + +OBJDEST=$(OBJDIR)/lib/base + +ifeq ($(ARCH), WINNT) +LIBS=$(OBJDIR)/lib/libbase.lib +ifeq ($(BSCINFO), yes) +BSCS=$(OBJDIR)/lib/libbase.bsc +endif +else +LIBS=$(OBJDIR)/lib/libbase.a +endif + +include ../../nsconfig.mk + +MCC_INCLUDE += $(ADMINUTIL_INCLUDE) + +LOCAL_DEPS = $(NSPR_DEP) $(ADMINUTIL_DEP) $(SECURITY_DEP) $(DBM_DEP) + +all: $(LOCAL_DEPS) $(OBJDEST) $(LIBS) $(BSCS) + +$(OBJDEST): + mkdir -p $(OBJDEST) + +ifeq ($(ARCH), WINNT) +OSOBJS = nterrors.o eventlog.o eventhandler.o ntpipe.o pathnames.o +else +OSOBJS = +endif + +OBJS = $(addprefix $(OBJDEST)/, \ + dstats.o \ + daemon.o \ + shexp.o \ + regexp.o \ + pblock.o \ + plist.o \ + buffer.o \ + netbuf.o \ + file.o \ + net.o \ + session.o \ + cinfo.o \ + util.o \ + ereport.o \ + sem.o \ + fsmutex.o \ + systhr.o \ + crit.o \ + rwlock.o \ + dns.o \ + dnsdmain.o \ + shmem.o \ + dll.o \ + servssl.o \ + lexer.o \ + restart.o \ + objndx.o \ + cache.o \ + pool.o \ + time_cache.o \ + dns_cache.o \ + system.o \ + xp.o \ + nscperror.o \ + language.o \ + fasttime.o \ + secpwd.o \ + $(OSOBJS) \ + ) + +MODULE_CFLAGS=-I$(NSROOT)/include/base + +ifeq ($(PRODUCT),"Netscape Catalog Server") +ifeq ($(ARCH), WINNT) +MCC_INCLUDE := $(subst -I$(MCOM_ROOT)/lib/libnet,,$(MCC_INCLUDE)) +endif +endif + +#$(OBJDEST)/fasttime.o : $(OBJDIR)/fasttime.o +# cp $< $@ + +$(LIBS): $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ + +ifeq ($(ARCH), WINNT) +ifeq ($(BSCINFO), yes) +$(BSCS): $(OBJS) + $(BSCMAKE) $(OBJDEST)/*.sbr +endif +endif + +include $(INCLUDE_DEPENDS) + diff --git a/lib/base/crit.cpp b/lib/base/crit.cpp new file mode 100644 index 00000000..06f5fba4 --- /dev/null +++ b/lib/base/crit.cpp @@ -0,0 +1,399 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * crit.c: Critical section abstraction. Used in threaded servers to protect + * areas where two threads can interfere with each other. + * + * Condvars are condition variables that are used for thread-thread + * synchronization. + * + * Rob McCool + */ + +#include "systems.h" + +#include "netsite.h" +#include "crit.h" +#include "pool.h" +#include "ereport.h" + +#include "base/dbtbase.h" + +#ifdef USE_NSPR +/* +#include "prmon.h" +#include "private/primpl.h" +*/ +#include "nspr.h" + +/* private/primpl.h is gone fromnspr21 19971028 */ +#if 0 +#ifdef XP_WIN32 +#include "private/primpl.h" +#endif /* XP_WIN32 */ +#endif + +#include "prthread.h" +#include "prlock.h" +#include "prcvar.h" +#include "prinrval.h" + +/* + * Defined to replace PR_Monitor() with PR_Lock(). + */ +typedef struct critical { + PRLock *lock; + PRUint32 count; + PRThread *owner; +} critical_t; + +typedef struct condvar { + critical_t *lock; + PRCondVar *cvar; +} condvar_t; + +#endif +/* -------------------------- critical sections --------------------------- */ + +/* Useful for ASSERTs only. Returns non-zero if the current thread is the owner. + */ +NSAPI_PUBLIC int crit_owner_is_me(CRITICAL id) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)id; + + return (crit->owner == PR_GetCurrentThread()); +#else + return 1; +#endif +} + +NSAPI_PUBLIC CRITICAL crit_init(void) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)PERM_MALLOC(sizeof(critical_t)) ; + + if (crit) { + if (!(crit->lock = PR_NewLock())) { + PERM_FREE(crit); + return NULL; + } + crit->count = 0; + crit->owner = 0; + } + return (void *) crit; +#else + return NULL; +#endif +} + +NSAPI_PUBLIC void crit_enter(CRITICAL id) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)id; + PRThread *me = PR_GetCurrentThread(); + + if ( crit->owner == me) { + NS_ASSERT(crit->count > 0); + crit->count++; + } + else { + PR_Lock(crit->lock); + NS_ASSERT(crit->count == 0); + crit->count = 1; + crit->owner = me; + } +#endif +} + +NSAPI_PUBLIC void crit_exit(CRITICAL id) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)id; + + if (crit->owner != PR_GetCurrentThread()) + return; + + if ( --crit->count == 0) { + crit->owner = 0; + PR_Unlock(crit->lock); + } + NS_ASSERT(crit->count >= 0); +#endif +} + +NSAPI_PUBLIC void crit_terminate(CRITICAL id) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)id; + + PR_DestroyLock((PRLock*)crit->lock); + PERM_FREE(crit); +#endif +} + + +/* ------------------------- condition variables -------------------------- */ + + +NSAPI_PUBLIC CONDVAR condvar_init(CRITICAL id) +{ +#ifdef USE_NSPR + critical_t *crit = (critical_t*)id; + + condvar_t *cvar = (condvar_t*)PERM_MALLOC(sizeof(condvar_t)) ; + + if (crit) { + cvar->lock = crit; + if ((cvar->cvar = PR_NewCondVar((PRLock *)cvar->lock->lock)) == 0) { + PERM_FREE(cvar); + return NULL; + } + } + return (void *) cvar; +#endif +} + +NSAPI_PUBLIC void condvar_wait(CONDVAR _cv) +{ +#ifdef USE_NSPR + condvar_t *cv = (condvar_t *)_cv; + /* Save away recursion count so we can restore it after the wait */ + int saveCount = cv->lock->count; + PRThread *saveOwner = cv->lock->owner; + + NS_ASSERT(cv->lock->owner == PR_GetCurrentThread()); + cv->lock->count = 0; + cv->lock->owner = 0; + + PR_WaitCondVar(cv->cvar, PR_INTERVAL_NO_TIMEOUT); + + cv->lock->count = saveCount; + cv->lock->owner = saveOwner; +#endif +} + + +NSAPI_PUBLIC void condvar_timed_wait(CONDVAR _cv, long secs) +{ +#ifdef USE_NSPR + condvar_t *cv = (condvar_t *)_cv; + /* Save away recursion count so we can restore it after the wait */ + int saveCount = cv->lock->count; + PRThread *saveOwner = cv->lock->owner; + + NS_ASSERT(cv->lock->owner == PR_GetCurrentThread()); + cv->lock->count = 0; + cv->lock->owner = 0; + + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; + if (secs > 0) + timeout = PR_SecondsToInterval(secs); + PR_WaitCondVar(cv->cvar, timeout); + + cv->lock->count = saveCount; + cv->lock->owner = saveOwner; +#endif +} + + + +NSAPI_PUBLIC void condvar_notify(CONDVAR _cv) +{ +#ifdef USE_NSPR + condvar_t *cv = (condvar_t *)_cv; + NS_ASSERT(cv->lock->owner == PR_GetCurrentThread()); + PR_NotifyCondVar(cv->cvar); +#endif +} + +NSAPI_PUBLIC void condvar_notifyAll(CONDVAR _cv) +{ +#ifdef USE_NSPR + condvar_t *cv = (condvar_t *)_cv; + NS_ASSERT(cv->lock->owner == PR_GetCurrentThread()); + PR_NotifyAllCondVar(cv->cvar); +#endif +} + +NSAPI_PUBLIC void condvar_terminate(CONDVAR _cv) +{ +#ifdef USE_NSPR + condvar_t *cv = (condvar_t *)_cv; + PR_DestroyCondVar(cv->cvar); + PERM_FREE(cv); +#endif +} + +/* ----------------------- Counting semaphores ---------------------------- */ +/* These are currently not listed in crit.h because they aren't yet used; + * although they do work. + */ + +/* + * XXXMB - these should be in NSPR. + * + */ + +#if defined(SOLARIS) && defined(HW_THREADS) +#include <synch.h> +typedef sema_t counting_sem_t; +#elif defined(IRIX) && defined(HW_THREADS) +#include <ulocks.h> +typedef usema_t *counting_sem_t; +#else +typedef struct counting_sem_t { + CRITICAL lock; + CRITICAL cv_lock; + CONDVAR cv; + int count; + int max; +} counting_sem_t; +#endif + +NSAPI_PUBLIC COUNTING_SEMAPHORE +cs_init(int initial_count) +{ + counting_sem_t *cs = (counting_sem_t *)PERM_MALLOC(sizeof(counting_sem_t)); +#if defined(SOLARIS) && defined(HW_THREADS) + if ( sema_init(cs, initial_count, USYNC_THREAD, NULL) < 0) { + ereport(LOG_FAILURE, XP_GetAdminStr(DBT_csInitFailureS_), system_errmsg()); + PERM_FREE(cs); + return NULL; + } + + return (COUNTING_SEMAPHORE)cs; +#elif defined(IRIX) && defined(HW_THREADS) + usptr_t *arena; + + usconfig(CONF_INITSIZE, 64*1024); + if ( (arena = usinit("/tmp/cs.locks")) == NULL) + return NULL; + + if ( (cs = (counting_sem_t *)usnewsema(arena, 0)) == NULL) + return NULL; + + return cs; + +#else + + cs->count = initial_count; + cs->lock = crit_init(); + cs->cv_lock = crit_init(); + cs->cv = condvar_init(cs->cv_lock); + + return (COUNTING_SEMAPHORE)cs; +#endif +} + +NSAPI_PUBLIC void +cs_terminate(COUNTING_SEMAPHORE csp) +{ + counting_sem_t *cs = (counting_sem_t *)csp; + +#if defined(SOLARIS) && defined(HW_THREADS) + if ( sema_destroy(cs) < 0 ) { + ereport(LOG_FAILURE, XP_GetAdminStr(DBT_csTerminateFailureS_), system_errmsg()); + } + PERM_FREE(cs); + return; +#elif defined(IRIX) && defined(HW_THREADS) + /* usfreesema() */ + return; +#else + condvar_terminate(cs->cv); + crit_terminate(cs->cv_lock); + crit_terminate(cs->lock); + PERM_FREE(cs); + + return; +#endif +} + +NSAPI_PUBLIC int +cs_wait(COUNTING_SEMAPHORE csp) +{ + counting_sem_t *cs = (counting_sem_t *)csp; + int ret; + +#if defined(SOLARIS) && defined(HW_THREADS) + if ( (ret = sema_wait(cs)) < 0 ) { + ereport(LOG_FAILURE, XP_GetAdminStr(DBT_csWaitFailureS_), system_errmsg()); + return -1; + } + return ret; +#elif defined(IRIX) && defined(HW_THREADS) + uspsema(cs); + return 0; +#else + crit_enter(cs->lock); + while ( cs->count == 0 ) { + crit_enter(cs->cv_lock); + crit_exit(cs->lock); + condvar_wait(cs->cv); + crit_exit(cs->cv_lock); + crit_enter(cs->lock); + } + ret = --(cs->count); + crit_exit(cs->lock); + + return 0; +#endif +} + +NSAPI_PUBLIC int +cs_trywait(COUNTING_SEMAPHORE csp) +{ + counting_sem_t *cs = (counting_sem_t *)csp; + int ret; + +#if defined(SOLARIS) && defined(HW_THREADS) + ret = sema_trywait(cs)?-1:0; + return ret; +#elif defined(IRIX) && defined(HW_THREADS) + ret = uscpsema(cs); + return (ret == 1)?0:-1; +#else + crit_enter(cs->lock); + if (cs->count > 0) { + ret = --(cs->count); + crit_exit(cs->lock); + return 0; + } + crit_exit(cs->lock); + return -1; + +#endif +} + +NSAPI_PUBLIC int +cs_release(COUNTING_SEMAPHORE csp) +{ + counting_sem_t *cs = (counting_sem_t *)csp; + int ret; + +#if defined(SOLARIS) && defined(HW_THREADS) + if ( (ret = sema_post(cs)) < 0 ) { + ereport(LOG_FAILURE, XP_GetAdminStr(DBT_csPostFailureS_), system_errmsg()); + return -1; + } + return ret; +#elif defined(IRIX) && defined(HW_THREADS) + usvsema(cs); + return 0; +#else + crit_enter(cs->lock); + ret = ++(cs->count); + if (cs->count == 1) { + crit_enter(cs->cv_lock); + condvar_notify(cs->cv); + crit_exit(cs->cv_lock); + } + crit_exit(cs->lock); + + return 0; +#endif +} diff --git a/lib/base/dns.cpp b/lib/base/dns.cpp new file mode 100644 index 00000000..f9e38886 --- /dev/null +++ b/lib/base/dns.cpp @@ -0,0 +1,176 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * dns.c: DNS resolution routines + * + * Rob McCool + */ +#define DNS_GUESSING + +#include "netsite.h" +#ifdef XP_UNIX +#include "systems.h" +#else /* XP_WIN32 */ +#include "base/systems.h" +#endif /* XP_WIN32 */ + +#include "net.h" /* SYS_NETFD, various headers to do DNS */ + +/* Under NT, these are taken care of by net.h including winsock.h */ +#ifdef XP_UNIX +#include <arpa/inet.h> /* inet_ntoa */ +#include <netdb.h> /* struct hostent */ +#ifdef NEED_GHN_PROTO +extern "C" int gethostname (char *name, size_t namelen); +#endif +#endif +#ifdef DNS_CACHE +#include "base/dns_cache.h" +#include "base/ereport.h" +#endif /* DNS_CACHE */ +#include <stdio.h> +#include <nspr.h> +#include "frame/conf.h" + +/* ---------------------------- dns_find_fqdn ----------------------------- */ + + +/* defined in dnsdmain.c */ +extern "C" NSAPI_PUBLIC char *dns_guess_domain(char * hname); + +char *net_find_fqdn(PRHostEnt *p) +{ + int x; + + if((!p->h_name) || (!p->h_aliases)) + return NULL; + + if(!strchr(p->h_name, '.')) { + for(x = 0; p->h_aliases[x]; ++x) { + if((strchr(p->h_aliases[x], '.')) && + (!strncmp(p->h_aliases[x], p->h_name, strlen(p->h_name)))) + { + return STRDUP(p->h_aliases[x]); + } + } +#ifdef DNS_GUESSING + return dns_guess_domain(p->h_name); +#else + return NULL; +#endif /* DNS_GUESSING */ + } + else + return STRDUP(p->h_name); +} + + +/* ----------------------------- dns_ip2host ------------------------------ */ + + +char *dns_ip2host(char *ip, int verify) +{ + /* struct in_addr iaddr; */ + PRNetAddr iaddr; + char *hn; +#ifdef DNS_CACHE + dns_cache_entry_t *dns_entry; +#endif + static unsigned long laddr = 0; + static char myhostname[256]; + PRHostEnt hent; + char buf[PR_NETDB_BUF_SIZE]; + PRStatus err; + + + err = PR_InitializeNetAddr(PR_IpAddrNull, 0, &iaddr); + + if((iaddr.inet.ip = inet_addr(ip)) == -1) + goto bong; + +#ifdef DNS_CACHE + if ( (dns_entry = dns_cache_lookup_ip((unsigned int)iaddr.inet.ip)) ) { + hn = NULL; + if ( dns_entry->host && + /* Only use entry if the cache entry has been verified or if + * verify is off... + */ + (dns_entry->verified || !verify) ) { + hn = STRDUP( dns_entry->host ); + (void)dns_cache_use_decrement(dns_entry); + return hn; + } + dns_cache_delete(dns_entry); + dns_entry = 0; + } +#endif + + /* + * See if it happens to be the localhost IP address, and try + * the local host name if so. + */ + if (laddr == 0) { + laddr = inet_addr("127.0.0.1"); + myhostname[0] = 0; + PR_GetSystemInfo(PR_SI_HOSTNAME, myhostname, sizeof(myhostname)); + } + + /* Have to match the localhost IP address and have a hostname */ + if ((iaddr.inet.ip == laddr) && (myhostname[0] != 0)) { + /* + * Now try for a fully-qualified domain name, starting with + * the local hostname. + */ + err = PR_GetHostByName(myhostname, + buf, + PR_NETDB_BUF_SIZE, + &hent); + + /* Don't verify if we get a fully-qualified name this way */ + verify = 0; + } + else { + err = PR_GetHostByAddr(&iaddr, + buf, + PR_NETDB_BUF_SIZE, + &hent); + } + + if ((err == PR_FAILURE) || !(hn = net_find_fqdn(&hent))) goto bong; + + + if(verify) { + char **haddr = 0; + err = PR_GetHostByName(hn, + buf, + PR_NETDB_BUF_SIZE, + &hent); + + if(err == PR_SUCCESS) { + for(haddr = hent.h_addr_list; *haddr; haddr++) { + if(((struct in_addr *)(*haddr))->s_addr == iaddr.inet.ip) + break; + } + } + + if((err == PR_FAILURE) || (!(*haddr))) + goto bong; + } + +#ifdef DNS_CACHE + if ( (dns_entry = dns_cache_insert(hn, (unsigned int)iaddr.inet.ip, verify)) ) { + (void) dns_cache_use_decrement(dns_entry); + } +#endif /* DNS_CACHE */ + return hn; + bong: +#ifdef DNS_CACHE + /* Insert the lookup failure */ + if ( (dns_entry = dns_cache_insert(NULL, (unsigned int)iaddr.inet.ip, verify)) ) { + (void) dns_cache_use_decrement(dns_entry); + } +#endif /* DNS_CACHE */ + return NULL; +} diff --git a/lib/base/dnsdmain.cpp b/lib/base/dnsdmain.cpp new file mode 100644 index 00000000..f37e475c --- /dev/null +++ b/lib/base/dnsdmain.cpp @@ -0,0 +1,159 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * dnsdmain.c: DNS domain guessing stuff moved out of dns.c because of the + * string ball problems + */ + + +#include "netsite.h" +#include "base/net.h" +#include <string.h> +#include <stdio.h> +#ifdef XP_UNIX +#include <unistd.h> +#endif +#include <ctype.h> +#include "util.h" + +/* Under NT, this is taken care of by net.h including winsock.h */ +#ifdef XP_UNIX +#include <netdb.h> /* struct hostent */ +#endif +extern "C" { +#include <nspr.h> +} +#include "frame/conf.h" + + + +/* This is contained in resolv.h on most systems. */ +#define _PATH_RESCONF "/etc/resolv.conf" + +#ifdef XP_UNIX +NSPR_BEGIN_EXTERN_C +#ifdef Linux +extern int getdomainname(char *, size_t); +#else +extern int getdomainname(char *, int); +#endif /* Linux */ +#if defined(HPUX) || defined (UnixWare) || defined(Linux) || defined(IRIX6_5) +extern int gethostname (char *name, size_t namelen); +#else +#ifndef AIX +extern int gethostname (char *name, int namelen); +#endif +#endif +NSPR_END_EXTERN_C +#endif + + +/* ---------------------------- dns_guess_domain -------------------------- */ + + +extern "C" NSAPI_PUBLIC char *dns_guess_domain(char * hname) +{ + FILE *f; + char * cp; + int hnlen; + char line[256]; + static int dnlen = 0; + static char * domain = 0; + PRHostEnt hent; + char buf[PR_NETDB_BUF_SIZE]; + PRStatus err; + + /* Sanity check */ + if (strchr(hname, '.')) { + return STRDUP(hname); + } + + if (dnlen == 0) { + + /* First try a little trick that seems to often work... */ + + /* + * Get the local host name, even it doesn't come back + * fully qualified. + */ + line[0] = 0; + gethostname(line, sizeof(line)); + if (line[0] != 0) { + /* Is it fully qualified? */ + domain = strchr(line, '.'); + if (domain == 0) { + /* No, try gethostbyname() */ + err = PR_GetHostByName(line, + buf, + PR_NETDB_BUF_SIZE, + &hent); + if (err == PR_SUCCESS) { + /* See if h_name is fully-qualified */ + if (hent.h_name) domain = strchr(hent.h_name, '.'); + + /* Otherwise look for a fully qualified alias */ + if ((domain == 0) && + (hent.h_aliases && hent.h_aliases[0])) { + char **p; + for (p = hent.h_aliases; *p; ++p) { + domain = strchr(*p, '.'); + if (domain) break; + } + } + } + } + } + + /* Still no luck? */ + if (domain == 0) { + + f = fopen(_PATH_RESCONF, "r"); + + /* See if there's a domain entry in their resolver configuration */ + if(f) { + while(fgets(line, sizeof(line), f)) { + if(!strncasecmp(line, "domain ", 7)) { + for (cp = &line[7]; *cp && isspace(*cp); ++cp) ; + if (*cp) { + domain = cp; + for (; *cp && !isspace(*cp); ++cp) ; + *cp = 0; + } + break; + } + } + fclose(f); + } + } + +#ifndef NO_DOMAINNAME + if (domain == 0) { + /* No domain found. Try getdomainname. */ + getdomainname(line, sizeof(line)); + if (line[0] != 0) domain = &line[0]; + } +#endif + + if (domain != 0) { + if (domain[0] == '.') ++domain; + domain = STRDUP(domain); + dnlen = strlen(domain); + } + else dnlen = -1; + } + + if (domain != 0) { + hnlen = strlen(hname); + if ((hnlen + dnlen + 2) <= sizeof(line)) { + strcpy(line, hname); + line[hnlen] = '.'; + strcpy(&line[hnlen+1], domain); + return STRDUP(line); + } + } + + return 0; +} diff --git a/lib/base/ereport.cpp b/lib/base/ereport.cpp new file mode 100644 index 00000000..3454d01f --- /dev/null +++ b/lib/base/ereport.cpp @@ -0,0 +1,249 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * ereport.c: Records transactions, reports errors to administrators, etc. + * + * Rob McCool + */ + + +#include "private/pprio.h" /* for nspr20 binary release */ +#include "netsite.h" +#include "file.h" /* system_fopenWA, system_write_atomic */ +#include "pblock.h" +#include "session.h" +#include "util.h" /* util_vsprintf */ +#include "ereport.h" + +#include "base/dbtbase.h" + +#include <stdarg.h> +#include <stdio.h> /* vsprintf */ +#include <string.h> /* strcpy */ +#include <time.h> /* localtime */ + +#ifdef XP_UNIX +#include <syslog.h> /* error logging to syslog */ + +static SYS_FILE _error_fd; +#else /* WIN32 */ +#include <nt/regparms.h> +#include <nt/messages.h> +#include "eventlog.h" + +static SYS_FILE _error_fd; +#endif /* XP_UNIX */ + +static PRBool _ereport_initialized = PR_FALSE; + + +NSAPI_PUBLIC SYS_FILE ereport_getfd(void) { return _error_fd; } + + + +/* ----------------------------- ereport_init ----------------------------- */ + + +NSAPI_PUBLIC char *ereport_init(char *err_fn, char *email, + PASSWD pwuser, char *version) +{ + char err[MAGNUS_ERROR_LEN]; + SYS_FILE new_fd; + +#ifdef XP_UNIX + if(!strcmp(err_fn, "SYSLOG")) { +#ifdef NET_SSL + openlog("secure-httpd", LOG_PID, LOG_DAEMON); +#else + openlog("httpd", LOG_PID, LOG_DAEMON); +#endif + _error_fd = PR_ImportFile(ERRORS_TO_SYSLOG); + + _ereport_initialized = PR_TRUE; + return NULL; + } +#endif /* XP_UNIX */ + if( (new_fd = system_fopenWA(err_fn)) == SYS_ERROR_FD) { + util_snprintf(err, MAGNUS_ERROR_LEN, "can't open error log %s (%s)", err_fn, + system_errmsg()); +#ifdef XP_UNIX + _error_fd = PR_ImportFile(STDERR_FILENO); +#else + _error_fd = PR_ImportFile(NULL); +#endif + return STRDUP(err); + } + _error_fd = new_fd; + +#ifdef XP_UNIX + if(pwuser) + chown(err_fn, pwuser->pw_uid, pwuser->pw_gid); +#endif /* XP_UNIX */ + + _ereport_initialized = PR_TRUE; + + ereport(LOG_INFORM, XP_GetAdminStr(DBT_successfulServerStartup_)); + ereport(LOG_INFORM, XP_GetAdminStr(DBT_SBS_), MAGNUS_VERSION_STRING, BUILD_NUM); + if (strcasecmp(BUILD_NUM, version)) { + ereport(LOG_WARN, XP_GetAdminStr(DBT_netscapeExecutableAndSharedLibra_)); + ereport(LOG_WARN, XP_GetAdminStr(DBT_executableVersionIsS_), version); + ereport(LOG_WARN, XP_GetAdminStr(DBT_sharedLibraryVersionIsS_), BUILD_NUM); + + } + + /* Initialize thread-specific error handling */ + system_errmsg_init(); + + return NULL; +} + + + +/* -------------------------- ereport_terminate --------------------------- */ + + +NSAPI_PUBLIC void ereport_terminate(void) +{ + if (!_ereport_initialized) + return; + +#ifdef XP_UNIX + ereport(LOG_INFORM, XP_GetAdminStr(DBT_errorReportingShuttingDown_)); + if(PR_FileDesc2NativeHandle(_error_fd) != ERRORS_TO_SYSLOG) + system_fclose(_error_fd); + else + closelog(); +#else /* WIN32 */ + if(PR_FileDesc2NativeHandle(_error_fd) != NULL) + system_fclose(_error_fd); +#endif /* XP_UNIX */ + +} + + +/* ------------------------------- ereport -------------------------------- */ + + +static int degree_msg[] = { + DBT_warning_, + DBT_config_, + DBT_security_, + DBT_failure_, + DBT_catastrophe_, + DBT_info_, + DBT_verbose_ + }; + +#ifdef XP_UNIX +static int degree_syslog[] = { + LOG_WARNING, LOG_ERR, LOG_NOTICE, LOG_ALERT, LOG_CRIT, LOG_INFO, LOG_INFO +}; +#endif/* XP_UNIX */ + + +NSAPI_PUBLIC int ereport_v(int degree, char *fmt, va_list args) +{ + char errstr[MAX_ERROR_LEN]; + int pos = 0; + struct tm *tms, tmss; + time_t t; +#ifdef MCC_PROXY + char *p; + int i; +#endif /* MCC_PROXY */ + + if (!_ereport_initialized) + return IO_OKAY; + +#ifdef XP_UNIX + if(PR_FileDesc2NativeHandle(_error_fd) != ERRORS_TO_SYSLOG) { +#endif /* XP_UNIX */ + t = time(NULL); + tms = system_localtime(&t, &tmss); + util_strftime(errstr, ERR_TIMEFMT, tms); + pos = strlen(errstr); + + pos += util_snprintf(&errstr[pos], MAX_ERROR_LEN - pos, " %s: ", + XP_GetAdminStr(degree_msg[degree])); +#ifdef XP_UNIX + } +#endif /* XP_UNIX */ + + pos += util_vsnprintf(&errstr[pos], sizeof(errstr) - pos, fmt, args); + + pos += util_snprintf(&errstr[pos], sizeof(errstr) - pos, ENDLINE); + +#ifdef MCC_PROXY + /* Thanx to netlib, the proxy sometimes generates multiline err msgs */ + for(p=errstr, i=pos-1; i>0; i--, p++) { + if (*p == '\n' || *p == '\r') *p = ' '; + } +#endif /* MCC_PROXY */ + +#if defined XP_UNIX + if(PR_FileDesc2NativeHandle(_error_fd) != ERRORS_TO_SYSLOG) + return system_fwrite_atomic(_error_fd, errstr, pos); + syslog(degree_syslog[degree], errstr); + return IO_OKAY; +#elif defined XP_WIN32 /* XP_WIN32 */ + if(PR_FileDesc2NativeHandle(_error_fd) != NULL) + { +#ifdef MCC_HTTPD + /* also write to NT Event Log if error is serious */ + switch (degree) + { + case LOG_MISCONFIG: + case LOG_SECURITY: + case LOG_CATASTROPHE: + LogErrorEvent(NULL, EVENTLOG_ERROR_TYPE, + 0, MSG_BAD_PARAMETER, + errstr, NULL); + break; + default: + break; + } +#endif + return system_fwrite_atomic(_error_fd, errstr, pos); + } + else { /* log to the EventLogger */ + /* Write to the event logger... */ + switch (degree) { + case LOG_WARN: + LogErrorEvent(NULL, EVENTLOG_WARNING_TYPE, + 0, MSG_BAD_PARAMETER, + errstr, NULL); + break; + case LOG_MISCONFIG: + case LOG_SECURITY: + case LOG_FAILURE: + case LOG_CATASTROPHE: + LogErrorEvent(NULL, EVENTLOG_ERROR_TYPE, + 0, MSG_BAD_PARAMETER, + errstr, NULL); + break; + + case LOG_INFORM: + default: + LogErrorEvent(NULL, EVENTLOG_INFORMATION_TYPE, + 0, MSG_BAD_PARAMETER, + errstr, NULL); + break; + } + return (IO_OKAY); + } +#endif /* XP_WIN32 */ + +} + +NSAPI_PUBLIC int ereport(int degree, char *fmt, ...) +{ + va_list args; + int rv; + va_start(args, fmt); + rv = ereport_v(degree, fmt, args); + va_end(args); + return rv; +} diff --git a/lib/base/eventlog.cpp b/lib/base/eventlog.cpp new file mode 100644 index 00000000..b8762842 --- /dev/null +++ b/lib/base/eventlog.cpp @@ -0,0 +1,70 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +// // +// Name: EVENTLOG // +// Platforms: WIN32 // +// ...................................................................... // +// Revision History: // +// 01-12-95 Initial Version, Aruna Victor (aruna@netscape.com) // +// 12-02-96 Code cleanup, Andy Hakim (ahakim@netscape.com) // +// - consolidated admin and http functions into one // +// - moved registry modification code to installer // +// - removed several unecessary functions // +// - changed function parameters to existing functions // +// // +//--------------------------------------------------------------------------// + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include "netsite.h" +#include "base/eventlog.h" +#include "frame/conf.h" +#include <nt/regparms.h> +#include <nt/messages.h> + +HANDLE ghEventSource; + +NSPR_BEGIN_EXTERN_C + +NSAPI_PUBLIC HANDLE InitializeLogging(char *szEventLogName) +{ + ghEventSource = RegisterEventSource(NULL, szEventLogName); + return ghEventSource; +} + + + +NSAPI_PUBLIC BOOL TerminateLogging(HANDLE hEventSource) +{ + BOOL bReturn = FALSE; + if(hEventSource == NULL) + hEventSource = ghEventSource; + if(hEventSource) + bReturn = DeregisterEventSource(hEventSource); + return(bReturn); +} + + + +NSAPI_PUBLIC BOOL LogErrorEvent(HANDLE hEventSource, WORD fwEventType, WORD fwCategory, DWORD IDEvent, LPTSTR chMsg, LPTSTR lpszMsg) +{ + BOOL bReturn = FALSE; + LPTSTR lpszStrings[2]; + + lpszStrings[0] = chMsg; + lpszStrings[1] = lpszMsg; + + if(hEventSource == NULL) + hEventSource = ghEventSource; + + if(hEventSource) + bReturn = ReportEvent(hEventSource, fwEventType, fwCategory, + IDEvent, NULL, 2, 0, (LPCTSTR *)lpszStrings, NULL); + return(bReturn); +} + +NSPR_END_EXTERN_C diff --git a/lib/base/file.cpp b/lib/base/file.cpp new file mode 100644 index 00000000..7382be46 --- /dev/null +++ b/lib/base/file.cpp @@ -0,0 +1,702 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * file.c: system specific functions for reading/writing files + * + * See file.h for formal definitions of what these functions do + * + * Rob McCool + */ + + +#include "base/file.h" +#include "ereport.h" +#ifdef BSD_RLIMIT +#include <sys/time.h> +#include <sys/resource.h> +#else +#include <stdlib.h> +#include <signal.h> +#endif +#ifdef XP_WIN32 +#include <time.h> /* time */ +#include <sys/stat.h> /* stat */ +#include <errno.h> +#include <direct.h> +#include <base/nterr.h> +/* Removed for ns security integration +#include <xp_error.h> +*/ +#endif +#include <prerror.h> + +#include "private/pprio.h" +#include "prlock.h" + +extern "C" char *nscperror_lookup(int err); + +/* --- globals -------------------------------------------------------------*/ +/* PRFileDesc * SYS_ERROR_FD = NULL; */ + +const int errbuf_size = 256; +const unsigned int LOCKFILERANGE=0x7FFFFFFF; +PRLock *_atomic_write_lock = NULL; + +/* --------------------------------- stat --------------------------------- */ + + + /* XXXMB - Can't convert to PR_GetFileInfo because we directly exported + * the stat interface... Damn. + */ +NSAPI_PUBLIC int system_stat(char *path, struct stat *finfo) +{ +#ifdef XP_WIN32 + + int chop, l; + +/* The NT stat is very peculiar about directory names. */ +/* XXX aruna - there is a bug here, maybe in the C runtime. + * Stating the same path in a separate program succeeds. From + * jblack's profiling, this needs to be replaced by the Win32 + * calls anyway.*/ + + l = strlen(path); + if((path[l - 1] == '/') && + (!(isalpha(path[0]) && (!strcmp(&path[1], ":/"))))) + { + chop = 1; + path[--l] = '\0'; + } + else chop = 0; +#endif /* XP_WIN32 */ + +#ifdef XP_UNIX + if(stat(path, finfo) == -1) + return -1; +#else /* XP_WIN32 */ + + if(_stat(path, (struct _stat *)finfo) == -1) { + /* XXXMB - this sucks; + * try to convert to an error code we'll expect... + */ + switch(errno) { + case ENOENT: PR_SetError(PR_FILE_NOT_FOUND_ERROR, errno); break; + default: PR_SetError(PR_UNKNOWN_ERROR, errno); break; + } + return -1; + } + + /* NT sets the time fields to -1 if it thinks that the file + * is a device ( like com1.html, lpt1.html etc) In this case + * simply set last modified time to the current time.... + */ + + if (finfo->st_mtime == -1) { + finfo->st_mtime = time(NULL); + } + if (finfo->st_atime == -1) { + finfo->st_atime = 0; + } + if (finfo->st_ctime == -1) { + finfo->st_ctime = 0; + } + if(chop) + path[l++] = '/'; + +#endif /* XP_WIN32 */ + + if(S_ISREG(finfo->st_mode) && (path[strlen(path) - 1] == '/')) { + /* File with trailing slash */ + errno = ENOENT; + return -1; + } + return 0; +} + + +NSAPI_PUBLIC int system_fread(SYS_FILE fd, char *buf, int sz) +{ + /* XXXMB - this is the *one* function which does return a length + * instead of the IO_ERROR/IO_OKAY. + */ + return PR_Read(fd, buf, sz); +} + +NSAPI_PUBLIC int system_fwrite(SYS_FILE fd, char *buf, int sz) { + int n,o,w; + + for(n=sz,o=0; n; n-=w,o+=w) { + if((w = PR_Write(fd, &buf[o], n)) < 0) + return IO_ERROR; + } + return IO_OKAY; +} + +/* ---------------------------- Standard UNIX ----------------------------- */ + +#ifdef XP_UNIX + +#include <sys/file.h> /* flock */ + +NSAPI_PUBLIC int system_fwrite_atomic(SYS_FILE fd, char *buf, int sz) +{ + int ret; +#if 0 + if(flock(fd,LOCK_EX) == -1) + return IO_ERROR; +#endif + ret = system_fwrite(fd,buf,sz); +#if 0 + if(flock(fd,LOCK_UN) == -1) + return IO_ERROR; /* ??? */ +#endif + return ret; +} + +/* -------------------------- system_nocoredumps -------------------------- */ + + +NSAPI_PUBLIC int system_nocoredumps(void) +{ +#ifdef BSD_RLIMIT + struct rlimit rl; + + rl.rlim_cur = 0; + rl.rlim_max = 0; + return setrlimit(RLIMIT_CORE, &rl); +#else +#if defined(SNI) +/* C++ compiler seems to find more that one overloaded instance of exit() ?! */ +#define EXITFUNC ::exit +#else +#define EXITFUNC exit +#endif + signal(SIGQUIT, EXITFUNC); + signal(SIGILL, EXITFUNC); + signal(SIGTRAP, EXITFUNC); + signal(SIGABRT, EXITFUNC); + signal(SIGIOT, EXITFUNC); + signal(SIGEMT, EXITFUNC); + signal(SIGFPE, EXITFUNC); + signal(SIGBUS, EXITFUNC); + signal(SIGSEGV, EXITFUNC); + signal(SIGSYS, EXITFUNC); + + + return 0; +#endif +} +#endif /* XP_UNIX */ + +/* --------------------------- file_setinherit ---------------------------- */ + +NSAPI_PUBLIC int file_setinherit(SYS_FILE fd, int value) +{ +#if defined(XP_WIN32) + int ret; + +// ret = SetHandleInformation((HANDLE)PR_FileDesc2NativeHandle(fd), 0, value?HANDLE_FLAG_INHERIT:0); + // This function did nothing before since the mask was set to 0. + ret = SetHandleInformation((HANDLE)PR_FileDesc2NativeHandle(fd), HANDLE_FLAG_INHERIT, value?HANDLE_FLAG_INHERIT:0); + return ret==0?-1:0; +#elif defined(XP_UNIX) + int flags = 0; + PRInt32 nativeFD; + PRFileDesc *bottom = fd; + + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + + nativeFD = PR_FileDesc2NativeHandle(bottom); +#if 0 +fprintf(stderr, "\nInfo(file_setinherit): Native file descriptor is %d\n", nativeFD); +#endif + flags = fcntl(nativeFD, F_GETFD, 0); + if(flags == -1) + return -1; + if(value) + flags &= (~FD_CLOEXEC); + else + flags |= FD_CLOEXEC; + fcntl(nativeFD, F_SETFD, flags); + return 0; + + /* Comment out for ns security/ nspr integration (HACK for NOW) + int flags = fcntl(PR_FileDesc2NativeHandle(fd), F_GETFD, 0); + if(flags == -1) + return -1; + if(value) + flags &= (~FD_CLOEXEC); + else + flags |= FD_CLOEXEC; + fcntl(PR_FileDesc2NativeHandle(fd), F_SETFD, flags); + return 0; + */ +#endif +} + +NSAPI_PUBLIC SYS_FILE system_fopenRO(char *p) +{ + SYS_FILE f = PR_Open(p, PR_RDONLY, 0); + + if (!f) + return SYS_ERROR_FD; + return f; +} + +NSAPI_PUBLIC SYS_FILE system_fopenWA(char *p) +{ + SYS_FILE f = PR_Open(p, PR_RDWR|PR_CREATE_FILE|PR_APPEND, 0644); + + if (!f) + return SYS_ERROR_FD; + return f; +} + +NSAPI_PUBLIC SYS_FILE system_fopenRW(char *p) +{ + SYS_FILE f = PR_Open(p, PR_RDWR|PR_CREATE_FILE, 0644); + + if (!f) + return SYS_ERROR_FD; + return f; +} + +NSAPI_PUBLIC SYS_FILE system_fopenWT(char *p) +{ + SYS_FILE f = PR_Open(p, PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0644); + + if (!f) + return SYS_ERROR_FD; + return f; +} + +NSAPI_PUBLIC int system_fclose(SYS_FILE fd) +{ + return (PR_Close(fd)); +} + + +#ifdef FILE_WIN32 + +int CgiBuffering; + +NSAPI_PUBLIC SYS_FILE system_fopen(char *path, int access, int flags) +{ + char p2[MAX_PATH]; + SYS_FILE ret; + HANDLE fd; + + if (strlen(path) >= MAX_PATH) { + return SYS_ERROR_FD; + } + + file_unix2local(path, p2); + + fd = CreateFile(p2, access, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, flags, 0, NULL); + ret = PR_ImportFile((int32)fd); + + if(ret == INVALID_HANDLE_VALUE) + return SYS_ERROR_FD; + + return ret; +} + + + +NSAPI_PUBLIC int system_pread(SYS_FILE fd, char *buf, int BytesToRead) { + unsigned long BytesRead = 0; + int result = 0; + BOOLEAN TimeoutSet = FALSE; + + /* XXXMB - nspr20 should be able to do this; but right now it doesn't + * return proper error info. + * fix it later... + */ + if(ReadFile((HANDLE)PR_FileDesc2NativeHandle(fd), (LPVOID)buf, BytesToRead, &BytesRead, NULL) == FALSE) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + return IO_EOF; + } else { + return IO_ERROR; + } + } + return (BytesRead ? BytesRead : IO_EOF); +} + +NSAPI_PUBLIC int system_pwrite(SYS_FILE fd, char *buf, int BytesToWrite) +{ + unsigned long BytesWritten; + + if (WriteFile((HANDLE)PR_FileDesc2NativeHandle(fd), (LPVOID)buf, + BytesToWrite, &BytesWritten, NULL) == FALSE) { + return IO_ERROR; + } + return BytesWritten; +} + + +NSAPI_PUBLIC int system_fwrite_atomic(SYS_FILE fd, char *buf, int sz) +{ + int ret; + +#if 0 + if(system_flock(fd) == IO_ERROR) + return IO_ERROR; +#endif + /* XXXMB - this is technically thread unsafe, but it catches any + * callers of fwrite_atomic when we're single threaded and just coming + * to life. + */ + if (!_atomic_write_lock) { + _atomic_write_lock = PR_NewLock(); + } + PR_Lock(_atomic_write_lock); + ret = system_fwrite(fd,buf,sz); + PR_Unlock(_atomic_write_lock); +#if 0 + if(system_ulock(fd) == IO_ERROR) + return IO_ERROR; +#endif + return ret; +} + + +NSAPI_PUBLIC void file_unix2local(char *path, char *p2) +{ + /* Try to handle UNIX-style paths */ + if((!strchr(path, FILE_PATHSEP))) { + int x; + + for(x = 0; path[x]; x++) + p2[x] = (path[x] == '/' ? '\\' : path[x]); + p2[x] = '\0'; + } + else + strcpy(p2, path); +} + + +NSAPI_PUBLIC int system_nocoredumps(void) +{ + return 0; +} + +/* --------------------------- system_winerr ------------------------------ */ + + +#include <winsock.h> +#include <errno.h> +#include "util.h" + +NSAPI_PUBLIC char *system_winsockerr(void) +{ + int errn = WSAGetLastError(); + + return FindError(errn); +} + +NSAPI_PUBLIC char *system_winerr(void) +{ + int errn = GetLastError(); + + if (errn == 0) + errn = WSAGetLastError(); + return FindError(errn); +} + +/* ------------------------- Dir related stuff ---------------------------- */ + + +NSAPI_PUBLIC SYS_DIR dir_open(char *pathp) +{ + dir_s *ret = (dir_s *) MALLOC(sizeof(dir_s)); + char path[MAX_PATH]; + int l; + + if (strlen(pathp) >= MAX_PATH) { + return NULL; + } + + l = util_sprintf(path, "%s", pathp) - 1; + path[strlen(pathp)] = '\0'; + if(path[strlen(path) - 1] != FILE_PATHSEP) + strcpy (path + strlen(path), "\\*.*"); + else + util_sprintf(path, "%s*.*", path); + + ret->de.d_name = NULL; + if( (ret->dp = FindFirstFile(path, &ret->fdata)) != INVALID_HANDLE_VALUE) + return ret; + FREE(ret); + return NULL; +} + +NSAPI_PUBLIC SYS_DIRENT *dir_read(SYS_DIR ds) +{ + if(FindNextFile(ds->dp, &ds->fdata) == FALSE) + return NULL; + if(ds->de.d_name) + FREE(ds->de.d_name); + ds->de.d_name = STRDUP(ds->fdata.cFileName); + + return &ds->de; +} + +NSAPI_PUBLIC void dir_close(SYS_DIR ds) +{ + FindClose(ds->dp); + if(ds->de.d_name) + FREE(ds->de.d_name); + FREE(ds); +} + +#endif /* FILE_WIN32 */ + +NSAPI_PUBLIC int file_notfound(void) +{ +#ifdef FILE_WIN32 + int errn = PR_GetError(); + return (errn == PR_FILE_NOT_FOUND_ERROR); +#else + return (errno == ENOENT); +#endif +} + +NSAPI_PUBLIC int dir_create_all(char *dir) +{ + struct stat fi; + char *t; + +#ifdef XP_WIN32 + t = dir + 3; +#else /* XP_UNIX */ + t = dir + 1; +#endif + while(1) { + t = strchr(t, FILE_PATHSEP); + if(t) *t = '\0'; + if(stat(dir, &fi) == -1) { + if(dir_create(dir) == -1) + return -1; + } + if(t) *t++ = FILE_PATHSEP; + else break; + } + return 0; +} + + +#ifdef XP_UNIX +#if !defined(SNI) && !defined(LINUX) +extern char *sys_errlist[]; +#endif /* SNI */ +#endif + +#ifdef NET_SSL + +#define ERRMSG_SIZE 35 +#ifdef THREAD_ANY +static int errmsg_key = -1; +#include "systhr.h" +/* Removed for ns security integration +#include "xp_error.h" +*/ +#else +static char errmsg[ERRMSG_SIZE]; +#endif + +#include "util.h" + +static char *_errmsg_new(int code) +{ + char *ret; +#ifdef THREAD_ANY + if(!(ret = (char *) systhread_getdata(errmsg_key))) { + ret = (char *) PERM_MALLOC(256); + systhread_setdata(errmsg_key, (void *)ret); + } +#else + ret = errmsg; +#endif + util_snprintf(ret, ERRMSG_SIZE, "libsec code %d", code); +#ifndef MCC_BATMAN + PR_SetError(0,0); +#endif + return ret; +} +#endif + + +void system_errmsg_init(void) +{ + if (errmsg_key == -1) { +#if defined(THREAD_ANY) && defined(NET_SSL) + errmsg_key = systhread_newkey(); +#endif +#ifdef XP_WIN32 + HashNtErrors(); +#endif + if (!_atomic_write_lock) + _atomic_write_lock = PR_NewLock(); + } +} + +NSAPI_PUBLIC int system_errmsg_fn(char **buff, size_t maxlen) +{ + char static_error[128]; + char *lmsg = 0; /* Local message pointer */ + size_t msglen = 0; + int sys_error = 0; + PRErrorCode nscp_error; +#ifdef XP_WIN32 + LPTSTR sysmsg = 0; +#endif + + + /* Grab the OS error message */ +#ifdef XP_WIN32 + sys_error = GetLastError(); +#else + sys_error = errno; +#endif + nscp_error = PR_GetError(); + + /* If there is a NSPR error, but it is "unknown", try to get the OSError + * and use that instead. + */ + if (nscp_error == PR_UNKNOWN_ERROR) + errno = PR_GetOSError(); + + if (nscp_error != 0 && nscp_error != PR_UNKNOWN_ERROR){ + char *nscp_error_msg; + + nscp_error_msg = nscperror_lookup(nscp_error); + if(nscp_error_msg){ + PR_SetError(0, 0); + lmsg = nscp_error_msg; + } else { + util_snprintf(static_error, ERRMSG_SIZE, "unknown error %d", nscp_error); + lmsg = static_error; + } + } else { +#if defined(XP_WIN32) + msglen = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + GetLastError(), + LOCALE_SYSTEM_DEFAULT, + (LPTSTR)&sysmsg, + 0, + 0); + if (msglen > 0) + lmsg = sysmsg; + else + lmsg = system_winerr(); + SetLastError(0); +#else +/* replaced +#if defined(SNI) || defined(LINUX) + / C++ platform has no definition for sys_errlist / + lmsg = strerror(errno); +#else + lmsg = sys_errlist[errno]; +#endif +with lmsg =strerror(errno);*/ + lmsg=strerror(errno); + errno = 0; +#endif + } + + /* At this point lmsg points to something. */ + msglen = strlen(lmsg); + + if (*buff == NULL) + *buff = STRDUP(lmsg); + else if (maxlen > msglen) + memcpy(*buff, lmsg, msglen+1); + else + msglen = 0; + +#ifdef XP_WIN32 + /* NT's FormatMessage() dynamically allocated the msg; free it */ + if (sysmsg) + LocalFree(sysmsg); +#endif + + return msglen; +} + +NSAPI_PUBLIC char * +system_errmsg(void) +{ + char *buff = 0; + + if (errmsg_key == -1) + return "unknown early startup error"; + + // rmaxwell - This is extremely lame. + // Allocate a buffer in thread local storage to + // hold the last error message. + // The whole error message facility is broken and should be + // updated to get error strings out of the code. + if(!(buff = (char *) systhread_getdata(errmsg_key))) { + buff = (char *) PERM_MALLOC(errbuf_size); + systhread_setdata(errmsg_key, (void *)buff); + } + system_errmsg_fn(&buff, errbuf_size); + if (buff == 0) + buff = "Could not retrieve system error message"; + return buff; +} + +NSAPI_PUBLIC int +system_rename(char *oldpath, char *newpath) +{ + return rename(oldpath, newpath); +} + +NSAPI_PUBLIC int +system_unlink(char *path) +{ + return PR_Delete(path)==PR_FAILURE?-1:0; +} + +NSAPI_PUBLIC int system_lseek(SYS_FILE fd, int off, int wh) +{ + switch (wh) { + case 0: + return PR_Seek(fd, off, PR_SEEK_SET); + break; + case 1: + return PR_Seek(fd, off, PR_SEEK_CUR); + break; + case 2: + return PR_Seek(fd, off, PR_SEEK_END); + break; + default: + return -1; + } +} + +NSAPI_PUBLIC int +system_tlock(SYS_FILE fd) +{ + return PR_TLockFile(fd); +} + +NSAPI_PUBLIC int +system_flock(SYS_FILE fd) +{ + return PR_LockFile(fd); +} + +NSAPI_PUBLIC int +system_ulock(SYS_FILE fd) +{ + return PR_UnlockFile(fd); +} diff --git a/lib/base/fsmutex.cpp b/lib/base/fsmutex.cpp new file mode 100644 index 00000000..06fe2403 --- /dev/null +++ b/lib/base/fsmutex.cpp @@ -0,0 +1,187 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * fsmutex: Mutexes that are filesystem-based so they're available from more + * than one process and address space + * + * Rob McCool + */ + +#include "base/fsmutex.h" +#ifdef THREAD_ANY +#include "base/crit.h" +#include "base/systhr.h" +#endif + +#include "base/util.h" +#ifdef XP_WIN32 +typedef HANDLE sys_fsmutex_t; +#endif + +#ifdef XP_UNIX +#include "base/file.h" +typedef SYS_FILE sys_fsmutex_t; +#endif + + +typedef struct { + sys_fsmutex_t mutex; + char *id; +#ifdef THREAD_ANY + CRITICAL crit; +#endif + int flags; +} fsmutex_s; + + + +/* ----------------------------- fsmutex_init ----------------------------- */ + + +#ifdef XP_UNIX +static int +_fsmutex_create(fsmutex_s *fsm, char *name, int number) +{ + char tn[256]; + SYS_FILE lfd; + int visible = (fsm->flags & FSMUTEX_VISIBLE ? 1 : 0); + + util_snprintf(tn, 256, "/tmp/%s.%d", name, number); + if(!visible) + unlink(tn); + if( (lfd = PR_Open(tn, PR_RDWR|PR_CREATE_FILE, 0644)) == NULL) + return -1; + + if(!visible) + unlink(tn); + else + fsm->id = PERM_STRDUP(tn); + fsm->mutex = lfd; + return 0; +} +#endif + +#ifdef XP_WIN32 +static int +_fsmutex_create(fsmutex_s *fsm, char *name, int number) +{ + char tn[256]; + util_snprintf(tn, sizeof(tn), "%s.%d", name, number); + + fsm->id = NULL; + fsm->mutex = CreateMutex(NULL, FALSE, + (fsm->flags & FSMUTEX_VISIBLE ? tn : NULL)); + return (fsm->mutex ? 0 : -1); +} +#endif + +NSAPI_PUBLIC FSMUTEX +fsmutex_init(char *name, int number, int flags) +{ + fsmutex_s *ret = (fsmutex_s *) PERM_MALLOC(sizeof(fsmutex_s)); + + ret->flags = flags; + if(_fsmutex_create(ret, name, number) == -1) { + PERM_FREE(ret); + return NULL; + } +#ifdef THREAD_ANY + if(flags & FSMUTEX_NEEDCRIT) + ret->crit = crit_init(); +#endif + return (FSMUTEX) ret; +} + +#ifdef XP_UNIX +NSAPI_PUBLIC void +fsmutex_setowner(FSMUTEX fsm, uid_t uid, gid_t gid) +{ + if(!geteuid()) + (void) chown( ((fsmutex_s *)fsm)->id, uid, gid); +} +#endif + + +/* -------------------------- fsmutex_terminate --------------------------- */ + + +#ifdef XP_UNIX +static void +_fsmutex_delete(fsmutex_s *fsm) +{ + if(fsm->flags & FSMUTEX_VISIBLE) + unlink(fsm->id); + PERM_FREE(fsm->id); + PR_Close(fsm->mutex); +} +#endif + +#ifdef XP_WIN32 +static void +_fsmutex_delete(fsmutex_s *fsm) +{ + CloseHandle(fsm->mutex); +} +#endif + +NSAPI_PUBLIC void +fsmutex_terminate(FSMUTEX id) +{ + fsmutex_s *fsm = (fsmutex_s *) id; + + _fsmutex_delete(fsm); +#ifdef THREAD_ANY + if(fsm->flags & FSMUTEX_NEEDCRIT) + crit_terminate(fsm->crit); +#endif + PERM_FREE(fsm); +} + + +/* ----------------------------- fsmutex_lock ----------------------------- */ + + +NSAPI_PUBLIC void +fsmutex_lock(FSMUTEX id) +{ + fsmutex_s *fsm = (fsmutex_s *) id; +#ifdef THREAD_ANY + if(fsm->flags & FSMUTEX_NEEDCRIT) + crit_enter(fsm->crit); +#endif +#ifdef XP_UNIX +#ifdef THREAD_NSPR_USER + /* Poll to avoid blocking. XXXrobm If errno is wrong this may go awry. */ + while(system_tlock(fsm->mutex) == -1) + systhread_sleep(1000); +#else + system_flock(fsm->mutex ); +#endif +#endif +#ifdef XP_WIN32 + WaitForSingleObject(fsm->mutex, INFINITE); +#endif +} + + +/* ---------------------------- fsmutex_unlock ---------------------------- */ + + +NSAPI_PUBLIC void +fsmutex_unlock(FSMUTEX id) +{ + fsmutex_s *fsm = (fsmutex_s *) id; +#ifdef XP_UNIX + system_ulock(fsm->mutex); +#endif +#ifdef XP_WIN32 + ReleaseMutex(fsm->mutex); +#endif +#ifdef THREAD_ANY + if(fsm->flags & FSMUTEX_NEEDCRIT) + crit_exit(fsm->crit); +#endif +} diff --git a/lib/base/lexer.cpp b/lib/base/lexer.cpp new file mode 100644 index 00000000..e521c51f --- /dev/null +++ b/lib/base/lexer.cpp @@ -0,0 +1,978 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (lexer.c) + * + * This module provides functions to assist parsers in lexical + * analysis. The idea is to provide a slightly higher-level + * interface than that of ctype.h. + */ + +#include "netsite.h" +#include "base/nsassert.h" + +#include "lexer_pvt.h" +#include "base/lexer.h" + +/* + * Description (lex_class_check) + * + * This function checks whether a given character belongs to one or + * specified character classes. + * + * Arguments: + * + * chtab - character class table pointer + * code - character code to be tested + * cbits - bit mask of character classes + * + * Returns: + * + * The return value is zero if the code is not in any of the character + * classes. It is non-zero, if the code is in at least one of the + * classes. + */ +NSAPI_PUBLIC +int lex_class_check(void * chtab, char code, unsigned long cbits) +{ + LEXClassTab_t * lct; /* character class table pointer */ + unsigned char * bp; /* bit vector pointer */ + int rv = 0; /* return value */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + bp = lct->lct_bv + code * lct->lct_bvbytes; + + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & cbits) { + rv = 1; + break; + } + cbits >>= 8; + } + + return rv; +} + +/* + * Description (lex_class_create) + * + * This function creates a new character class table. A + * character class table is used to map a character code to a + * set of character classes. The mapping for a given character + * is expressed as a bit vector, where each bit indicates the + * membership of that character in one of the character classes. + * + * Arguments: + * + * classc - the number of character classes being defined + * classv - pointers to null-terminated strings containing + * the character codes in each character class + * pchtab - indicates where to store a returned handle for + * the character class table + * + * Returns: + * + * If successful, the return value is the number of character + * classes specified (classc), and a handle for the created table + * is returned through pchtab. + * + * Usage Notes: + * + * Null (\000) can never be in any character classes, since it + * marks the end of the classv[] strings. + * + * classv[] can included NULL pointers, in which case bits will be + * allocated for corresponding empty character classes. + */ +NSAPI_PUBLIC +int lex_class_create(int classc, char * classv[], void **pchtab) +{ + int ncodes = 128; /* number of character encodings */ + int bvbytes; /* bytes per bit vector */ + LEXClassTab_t * ct; /* class table pointer */ + unsigned char * bp; /* bit vector pointer */ + char * cp; /* class string pointer */ + int bitmask; /* class bit mask */ + int bnum; /* byte number in bit vector */ + int ci; /* character index */ + int i; /* class index */ + + /* Get number of bytes per bit vector */ + NS_ASSERT(classc > 0); + bvbytes = (classc + 7) >> 3; + + /* Allocate the character class table */ + ct = (LEXClassTab_t *)calloc(1, sizeof(LEXClassTab_t) + ncodes * bvbytes); + if (ct == NULL) { + + /* Error - insufficient memory */ + return LEXERR_MALLOC; + } + + /* Initialize the class table */ + ct->lct_classc = classc; + ct->lct_bvbytes = bvbytes; + ct->lct_bv = (unsigned char *)(ct + 1); + + /* Initialize the bit vectors */ + for (i = 0; i < classc; ++i) { + + cp = classv[i]; + if (cp != NULL) { + + bitmask = 1 << (i & 7); + bnum = i >> 7; + + while ((ci = *cp++) != 0) { + bp = ct->lct_bv + ci + bnum; + *bp |= bitmask; + } + } + } + + /* Return pointer to table */ + NS_ASSERT(pchtab != NULL); + *pchtab = (void *)ct; + + return classc; +} + +NSAPI_PUBLIC +void lex_class_destroy(void * chtab) +{ + FREE((void *)chtab); +} + +NSAPI_PUBLIC +LEXStream_t * lex_stream_create(LEXStreamGet_t strmget, void * strmid, + char * buf, int buflen) +{ + LEXStream_t * lst; /* stream structure pointer */ + + /* Allocate the stream structure */ + lst = (LEXStream_t *)MALLOC(sizeof(LEXStream_t)); + if (lst == NULL) { + /* Error - insufficient memory */ + return 0; + } + + lst->lst_strmid = strmid; + lst->lst_get = strmget; + + /* + * Allocate a buffer for the stream if there's a positive length + * but a NULL buffer pointer. + */ + if ((buflen > 0) && (buf == NULL)) { + + buf = (char *)MALLOC(buflen); + if (buf == NULL) { + FREE((void *)lst); + return 0; + } + + /* Also initialize the current position and residual length */ + lst->lst_cp = buf; + lst->lst_len = 0; + lst->lst_flags = LST_FREEBUF; + } + + lst->lst_buf = buf; + lst->lst_buflen = buflen; + + return lst; +} + +NSAPI_PUBLIC +void lex_stream_destroy(LEXStream_t * lst) +{ + if ((lst->lst_flags & LST_FREEBUF) && (lst->lst_buf != NULL)) { + FREE(lst->lst_buf); + } + FREE((void *)lst); +} + +/* + * Description (lex_token_new) + * + * This function creates a new token object. A token object is + * used to accumulate text in an associated buffer. If the + * 'growlen' argument is specified as a value that is greater + * than zero, then the token buffer will be reallocated as + * necessary to accomodate more text. The initial size of + * the token buffer is given by 'initlen', which may be zero, + * and should be zero if lex_token_setbuf() is used. + * + * The token object is allocated from the memory pool given + * by the 'pool' argument. The default pool for the current + * thread is used if 'pool' is null. + * + * Arguments: + * + * pool - handle for memory pool to be used + * initlen - initial length of token buffer + * growlen - amount to grow a full token buffer + * token - pointer to returned token handle + * + * Returns: + * + * If successful, the function return value is zero and a handle + * for the new token is returned via 'token'. Otherwise a negative + * error code is returned. + */ + +NSAPI_PUBLIC +int lex_token_new(pool_handle_t * pool, int initlen, int growlen, void **token) +{ + LEXToken_t * lt; /* new token pointer */ + + /* Allocate the token structure */ + if (pool) { + lt = (LEXToken_t *)pool_calloc(pool, 1, sizeof(LEXToken_t)); + } + else { + lt = (LEXToken_t *)CALLOC(sizeof(LEXToken_t)); + } + if (lt == NULL) { + /* Error - insufficient memory */ + return LEXERR_MALLOC; + } + + /* Save the memory pool handle for future allocations */ + lt->lt_mempool = pool; + + /* Allocate the initial token buffer if initlen > 0 */ + if (initlen > 0) { + if (pool) { + lt->lt_buf = (char *)pool_malloc(pool, initlen); + } + else { + lt->lt_buf = (char *)MALLOC(initlen); + } + if (lt->lt_buf == NULL) { + /* Error - insufficient memory */ + if (pool) { + pool_free(pool, (void *)lt); + } + else { + FREE((void *)lt); + } + return LEXERR_MALLOC; + } + + lt->lt_initlen = initlen; + lt->lt_buflen = initlen; + lt->lt_buf[0] = 0; + } + + if (growlen > 0) lt->lt_inclen = growlen; + + NS_ASSERT(token != NULL); + *token = (void *)lt; + + return 0; +} + +/* + * Description (lex_token_start) + * + * This function discards any current contents of the token buffer + * associated with a specified token object, so that any new data + * appended to the token will start at the beginning of the token + * buffer. If there is no token buffer currently associated with + * the token, and the 'initlen' value specified to lex_token_new() + * was greater than zero, then a new token buffer is allocated. + * This function enables a token and optionally its token buffer + * to be reused. + * + * Arguments: + * + * token - handle for token object + * + * Returns: + * + * If successful, the function return value is zero. Otherwise + * a negative error code is returned. + */ + +NSAPI_PUBLIC int +lex_token_start(void * token) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + + /* Do we need to allocate a token buffer? */ + if ((lt->lt_buf == NULL) && (lt->lt_initlen > 0)) { + + /* Allocate the initial token buffer */ + if (lt->lt_mempool) { + lt->lt_buf = (char *)pool_malloc(lt->lt_mempool, lt->lt_initlen); + } + else { + lt->lt_buf = (char *)MALLOC(lt->lt_initlen); + } + if (lt->lt_buf == NULL) { + /* Error - insufficient memory */ + return LEXERR_MALLOC; + } + lt->lt_buflen = lt->lt_initlen; + } + + lt->lt_len = 0; + lt->lt_buf[0] = 0; + + return 0; +} + +/* + * Description (lex_token_info) + * + * This function returns information about the token buffer currently + * associated with a token object. This includes a pointer to the + * token data, if any, the current length of the token data, and the + * current size of the token buffer. + * + * Arguments: + * + * token - handle for token object + * tdatalen - pointer to returned token data length + * (may be null) + * tbufflen - pointer to returned token buffer length + * (may be null) + * + * Returns: + * + * The function return value is a pointer to the beginning of the + * token data, or null if there is no token buffer associated with + * the token. The token data length and token buffer length are + * returned via 'tdatalen' and 'tbufflen', respectively. + */ + +NSAPI_PUBLIC +char * lex_token_info(void * token, int * tdatalen, int * tbufflen) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + + if (tdatalen) *tdatalen = lt->lt_len; + if (tbufflen) *tbufflen = lt->lt_buflen; + + return lt->lt_buf; +} + +/* + * Description (lex_token) + * + * This function returns a pointer to the current token buffer, if any. + * If the length of the token is also needed, use lex_token_info(). + * This function would normally be used when the token is a + * null-terminated string. See also lex_token_take(). + * + * Arguments: + * + * token - handle for token object + * + * Returns: + * + * A pointer to the beginning of the current token is returned. + * The pointer is null if no token buffer is currently associated + * with the token object. + */ + +NSAPI_PUBLIC +char * lex_token(void * token) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + + return lt->lt_buf; +} + +/* + * Description (lex_token_destroy) + * + * This function destroys a specified token object. The memory + * associated with the token object and its token buffer, if any, + * is freed to whence it came. Note that token objects can be + * associated with a memory pool, and destroyed implicitly when + * the pool is destroyed via pool_destroy(). + * + * Arguments: + * + * token - handle for token object + */ + +NSAPI_PUBLIC +void lex_token_destroy(void * token) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + + if (lt) { + if (lt->lt_mempool) { + if (lt->lt_buf) { + pool_free(lt->lt_mempool, (void *)(lt->lt_buf)); + } + pool_free(lt->lt_mempool, (void *)lt); + } + else { + if (lt->lt_buf) { + FREE(lt->lt_buf); + } + FREE(lt); + } + } +} + +/* + * Description (lex_token_get) + * + * This function returns a pointer to the current token buffer, + * leaving the token with no associated token buffer. The caller + * assumes ownership of the returned token buffer. The length + * of the token data and the length of the token buffer are returned + * if requested. Note that lex_token_take() performs a similar + * operation. + * + * Arguments: + * + * token - handle for token object + * tdatalen - pointer to returned token data length + * (may be null) + * tbufflen - pointer to returned token buffer length + * (may be null) + * + * Returns: + * + * The function return value is a pointer to the beginning of the + * token data, or null if there is no token buffer associated with + * the token. The token data length and token buffer length are + * returned via 'tdatalen' and 'tbufflen', respectively. + */ + +NSAPI_PUBLIC +char * lex_token_get(void * token, int * tdatalen, int * tbufflen) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + char * tokenstr; + + tokenstr = lt->lt_buf; + if (tdatalen) *tdatalen = lt->lt_len; + if (tbufflen) *tbufflen = lt->lt_buflen; + + lt->lt_buf = NULL; + lt->lt_buflen = 0; + lt->lt_len = 0; + + return tokenstr; +} + +/* + * Description (lex_token_take) + * + * This function returns a pointer to the current token buffer, + * leaving the token with no associated token buffer. The caller + * assumes ownership of the returned token buffer. Note that + * lex_token_get() performs a similar operation, but returns more + * information. + * + * Arguments: + * + * token - handle for token object + * + * Returns: + * + * A pointer to the beginning of the current token is returned. + * The pointer is null if no token buffer is currently associated + * with the token object. + */ + +NSAPI_PUBLIC +char * lex_token_take(void * token) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + char * tokenstr; + + tokenstr = lt->lt_buf; + + lt->lt_buf = NULL; + lt->lt_buflen = 0; + lt->lt_len = 0; + + return tokenstr; +} + +/* + * Description (lex_token_append) + * + * This function appends data to the end of a token. If 'growlen' + * was specified as a greater-than-zero value for lex_token_new(), + * then the token buffer may be reallocated to accomodate the + * new data if necessary. A null byte is maintained in the token + * buffer following the token data, but it is not included in the + * token data length. + * + * Arguments: + * + * token - handle for token object + * nbytes - number of bytes of new data + * src - pointer to new data + * + * Returns: + * + * If successful, the function return value is the new length of + * the token data. Otherwise a negative error code is returned. + */ + +NSAPI_PUBLIC +int lex_token_append(void * token, int nbytes, char * src) +{ + LEXToken_t * lt = (LEXToken_t *)token; /* token pointer */ + int bufsize; + int length; + + NS_ASSERT(nbytes >= 0); + NS_ASSERT((src != NULL) || (nbytes == 0)); + + if (nbytes > 0) { + + bufsize = lt->lt_buflen; + length = lt->lt_len + nbytes; + + if (length >= bufsize) { + + while (length >= bufsize) { + bufsize += lt->lt_inclen; + } + + if (lt->lt_mempool) { + if (lt->lt_buf) { + lt->lt_buf = (char *)pool_realloc(lt->lt_mempool, + lt->lt_buf, bufsize); + } + else { + lt->lt_buf = (char *)pool_malloc(lt->lt_mempool, bufsize); + } + } + else { + if (lt->lt_buf) { + lt->lt_buf = (char *)REALLOC(lt->lt_buf, bufsize); + } + else { + lt->lt_buf = (char *)MALLOC(bufsize); + } + } + } + + if (lt->lt_buf) { + + memcpy((void *)(lt->lt_buf + lt->lt_len), (void *)src, nbytes); + lt->lt_buf[length] = 0; + lt->lt_len = length; + lt->lt_buflen = bufsize; + } + else { + /* Error - insufficient memory */ + return LEXERR_MALLOC; + } + } + + return lt->lt_len; +} + +NSAPI_PUBLIC +int lex_next_char(LEXStream_t * lst, void * chtab, unsigned long cbits) +{ + LEXClassTab_t * lct; /* character class table pointer */ + unsigned char * bp; /* bit vector pointer */ + unsigned long bitmask; /* class bit mask temporary */ + int rv; /* return value */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + /* Get the next character from the buffer */ + rv = *lst->lst_cp; + + bitmask = cbits; + bp = lct->lct_bv + rv * lct->lct_bvbytes; + + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & bitmask) { + /* Update the buffer pointer and length */ + lst->lst_cp += 1; + lst->lst_len -= 1; + break; + } + bitmask >>= 8; + } + + return rv; +} + +NSAPI_PUBLIC +int lex_scan_over(LEXStream_t * lst, void * chtab, unsigned long cbits, + void * token) +{ + LEXClassTab_t * lct; /* character class table pointer */ + char * cp; /* current pointer in stream buffer */ + unsigned char * bp; /* bit vector pointer */ + unsigned long bitmask; /* class bit mask temporary */ + int cv = 0; /* current character value */ + int rv = 0; /* return value */ + int slen; /* token segment length */ + int done = 0; /* done indication */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + while (!done) { + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + slen = 0; + cp = lst->lst_cp; + + while (slen < lst->lst_len) { + cv = *cp; + bitmask = cbits; + bp = lct->lct_bv + cv * lct->lct_bvbytes; + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & bitmask) goto more_token; + bitmask >>= 8; + } + + done = 1; + break; + + more_token: + slen += 1; + cp += 1; + } + + /* If the current segment is not empty, append it to the token */ + if (slen > 0) { + rv = lex_token_append(token, slen, lst->lst_cp); + if (rv < 0) break; + + /* Update the stream buffer pointer and length */ + lst->lst_cp += slen; + lst->lst_len -= slen; + } + } + + return ((rv < 0) ? rv : cv); +} + +/* + * Description (lex_scan_string) + * + * This function parses a quoted string into the specified token. + * The current character in the LEX stream is taken to be the + * beginning quote character. The quote character may be included + * in the string by preceding it with a '\'. Any newline + * characters to be included in the string must also be preceded + * by '\'. The string is terminated by another occurrence of the + * quote character, or an unquoted newline, or EOF. + * + * Arguments: + * + * lst - pointer to LEX stream structure + * token - handle for token + * flags - bit flags (unused - must be zero) + * + * Returns: + * + * The terminating character is returned, or zero if EOF. The + * string is returned in the token, without the beginning and + * ending quote characters. An error is indicated by a negative + * return value. + */ + +NSAPI_PUBLIC +int lex_scan_string(LEXStream_t * lst, void * token, int flags) +{ + char * cp; /* current pointer in stream buffer */ + int cv; /* current character value */ + int rv; /* return value */ + int slen; /* token segment length */ + int done = 0; /* done indication */ + int cquote = 0; /* character quote indication */ + int qchar = -1; /* quote character */ + + while (!done) { + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + slen = 0; + cp = lst->lst_cp; + + while (slen < lst->lst_len) { + + /* Get the next character */ + cv = *cp; + + /* Pick up the quote character if we don't have it yet */ + if (qchar < 0) { + qchar = cv; + + /* Don't include it in the string */ + lst->lst_cp += 1; + lst->lst_len -= 1; + cp += 1; + continue; + } + + /* cquote is 1 if the last character was '\' */ + if (cquote == 0) { + + /* Is this a string terminator? */ + if ((cv == qchar) || (cv == '\n')) { + + /* Append whatever we have to this point */ + if (slen > 0) goto append_it; + + /* + * If the terminator is the expected quote character, + * just skip it. If it's anything else, leave it as + * the current character. + */ + if (cv == qchar) { + lst->lst_cp += 1; + lst->lst_len -= 1; + } + + done = 1; + goto append_it; + } + + /* Got the character quote character? */ + if (cv == '\\') { + + /* Append anything we have so far first */ + if (slen > 0) goto append_it; + + /* Then skip the character */ + cquote = 1; + lst->lst_cp += 1; + lst->lst_len -= 1; + cp += 1; + continue; + } + } + else { + + /* Include any character following '\' */ + cquote = 0; + } + + /* Include this character in the string */ + slen += 1; + cp += 1; + } + + append_it: + + /* If the current segment is not empty, append it to the token */ + if (slen > 0) { + rv = lex_token_append(token, slen, lst->lst_cp); + if (rv < 0) break; + + /* Update the stream buffer pointer and length */ + lst->lst_cp += slen; + lst->lst_len -= slen; + } + } + + return ((rv < 0) ? rv : cv); +} + +NSAPI_PUBLIC +int lex_scan_to(LEXStream_t * lst, void * chtab, unsigned long cbits, + void * token) +{ + LEXClassTab_t * lct; /* character class table pointer */ + unsigned char * bp; /* bit vector pointer */ + char * cp; /* current pointer in stream buffer */ + unsigned long bitmask; /* class bit mask temporary */ + int cv = 0; /* current character value */ + int rv = 0; /* return value */ + int slen; /* token segment length */ + int done = 0; /* done indication */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + while (!done) { + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + slen = 0; + cp = lst->lst_cp; + + while (slen < lst->lst_len) { + cv = *cp; + bitmask = cbits; + bp = lct->lct_bv + cv * lct->lct_bvbytes; + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & bitmask) { + done = 1; + goto append_it; + } + bitmask >>= 8; + } + + slen += 1; + cp += 1; + } + + append_it: + + /* If the current segment is not empty, append it to the token */ + if (slen > 0) { + rv = lex_token_append(token, slen, lst->lst_cp); + if (rv < 0) break; + + /* Update the stream buffer pointer and length */ + lst->lst_cp += slen; + lst->lst_len -= slen; + } + } + + return ((rv < 0) ? rv : cv); +} + +NSAPI_PUBLIC +int lex_skip_over(LEXStream_t * lst, void * chtab, unsigned long cbits) +{ + LEXClassTab_t * lct; /* character class table pointer */ + unsigned char * bp; /* bit vector pointer */ + char * cp; /* current pointer in stream buffer */ + unsigned long bitmask; /* class bit mask temporary */ + int rv = 0; /* return value */ + int slen; /* token segment length */ + int done = 0; /* done indication */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + while (!done) { + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + slen = 0; + cp = lst->lst_cp; + + while (slen < lst->lst_len) { + rv = *cp; + bitmask = cbits; + bp = lct->lct_bv + rv * lct->lct_bvbytes; + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & bitmask) goto next_ch; + bitmask >>= 8; + } + + done = 1; + break; + + next_ch: + slen += 1; + cp += 1; + } + + if (slen > 0) { + /* Update the stream buffer pointer and length */ + lst->lst_cp += slen; + lst->lst_len -= slen; + } + } + + return rv; +} + +NSAPI_PUBLIC +int lex_skip_to(LEXStream_t * lst, void * chtab, unsigned long cbits) +{ + LEXClassTab_t * lct; /* character class table pointer */ + unsigned char * bp; /* bit vector pointer */ + char * cp; /* current pointer in stream buffer */ + unsigned long bitmask; /* class bit mask temporary */ + int rv; /* return value */ + int slen; /* token segment length */ + int done = 0; /* done indication */ + int i; /* loop index */ + + lct = (LEXClassTab_t *)chtab; + + while (!done) { + + /* Go get more stream data if none left in the buffer */ + if (lst->lst_len <= 0) { + rv = (*lst->lst_get)(lst); + if (rv <= 0) { + return rv; + } + } + + slen = 0; + cp = lst->lst_cp; + + while (slen < lst->lst_len) { + rv = *cp; + bitmask = cbits; + bp = lct->lct_bv + rv * lct->lct_bvbytes; + for (i = 0; i < lct->lct_bvbytes; ++i) { + if (*bp++ & bitmask) { + done = 1; + goto update_it; + } + bitmask >>= 8; + } + slen += 1; + cp += 1; + } + + update_it: + /* Update the stream buffer pointer and length */ + if (slen > 0) { + lst->lst_cp += slen; + lst->lst_len -= slen; + } + } + + return rv; +} diff --git a/lib/base/lexer_pvt.h b/lib/base/lexer_pvt.h new file mode 100644 index 00000000..d4a43f38 --- /dev/null +++ b/lib/base/lexer_pvt.h @@ -0,0 +1,30 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef __LEXER_PVT_H +#define __LEXER_PVT_H + +#ifndef _POOL_H_ +#include "base/pool.h" +#endif /* _POOL_H_ */ + +typedef struct LEXClassTab_s LEXClassTab_t; +struct LEXClassTab_s { + int lct_classc; /* number of character classes */ + int lct_bvbytes; /* number of bytes per bit vector */ + unsigned char * lct_bv; /* pointer to bit vector area */ +}; + +typedef struct LEXToken_s LEXToken_t; +struct LEXToken_s { + char * lt_buf; /* token buffer pointer */ + int lt_len; /* length of token data */ + int lt_buflen; /* current length of buffer */ + int lt_inclen; /* buffer length increment */ + int lt_initlen; /* initial length of token buffer */ + pool_handle_t * lt_mempool; /* token memory pool */ +}; + +#endif /* __LEXER_PVT_H */ diff --git a/lib/base/net.cpp b/lib/base/net.cpp new file mode 100644 index 00000000..0d15b121 --- /dev/null +++ b/lib/base/net.cpp @@ -0,0 +1,578 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * net.c: sockets abstraction and DNS related things + * + * Note: sockets created with net_socket are placed in non-blocking mode, + * however this API simulates that the calls are blocking. + * + * Rob McCool + */ + + +#include "netsite.h" +#include "prio.h" +#include "private/pprio.h" +#include <nspr.h> + +#include <frame/conf.h> +/* Removed for ns security integration +#include "sslio/sslio.h" +*/ + +#include "net.h" +#include "util.h" +#include "daemon.h" /* child_exit */ +#include "ereport.h" /* error reporting */ +#include <string.h> +#ifdef XP_UNIX +#include <arpa/inet.h> /* inet_ntoa */ +#include <netdb.h> /* hostent stuff */ +#ifdef NEED_GHN_PROTO +extern "C" int gethostname (char *name, size_t namelen); +#endif +#endif /* XP_UNIX */ +#ifdef LINUX +#include <sys/ioctl.h> /* ioctl */ +#endif + +extern "C" { +#include "ssl.h" +} + +#if defined(OSF1) +#include <stropts.h> +#endif +#include "base/systems.h" +#include "base/dbtbase.h" + +#if defined(OSF1) +#include <stropts.h> +#endif + +#ifdef IRIX +#include <bstring.h> /* fd_zero uses bzero */ +#endif +#include "netio.h" + +net_io_t net_io_functions; +/* removed for ns security integration +#include "xp_error.h" +*/ + +#include "libadmin/libadmin.h" + +int net_enabledns = 1; +int net_enableAsyncDNS = 0; +int net_listenqsize = DAEMON_LISTEN_SIZE; +unsigned int NET_BUFFERSIZE = NET_DEFAULT_BUFFERSIZE; +unsigned int NET_READ_TIMEOUT = NET_DEFAULT_READ_TIMEOUT; +unsigned int NET_WRITE_TIMEOUT = NET_DEFAULT_WRITE_TIMEOUT; +unsigned int SSL_HANDSHAKE_TIMEOUT = SSL_DEFAULT_HANDSHAKE_TIMEOUT; + + +/* ------------------------------ net_init -------------------------------- */ +NSAPI_PUBLIC int net_init(int security_on) +{ + return 0; +} + +/* ------------------------------ net_socket ------------------------------ */ +NSAPI_PUBLIC SYS_NETFD net_socket(int domain, int type, int protocol) +{ + SYS_NETFD sock; + SYS_NETFD prsock; + + if (security_active) { + if (protocol == IPPROTO_TCP) + prsock = PR_NewTCPSocket(); + else + prsock = PR_NewUDPSocket(); + if(prsock) + sock = SSL_ImportFD(NULL, prsock); + else sock = NULL; + } + else { + if (protocol == IPPROTO_TCP) sock = PR_NewTCPSocket(); + else sock = PR_NewUDPSocket(); + } + + if (sock == NULL) + return (SYS_NETFD)SYS_NET_ERRORFD; + return sock; +} + + +/* ---------------------------- net_getsockopt ---------------------------- */ +NSAPI_PUBLIC int net_getsockopt(SYS_NETFD s, int level, int optname, + void *optval, int *optlen) +{ + return getsockopt(PR_FileDesc2NativeHandle(s), level, optname, + (char *)optval, (TCPLEN_T *)optlen); +} + + +/* ---------------------------- net_setsockopt ---------------------------- */ + + +NSAPI_PUBLIC int net_setsockopt(SYS_NETFD s, int level, int optname, + const void *optval, int optlen) +{ + return setsockopt(PR_FileDesc2NativeHandle(s), level, optname, + (const char *)optval, optlen); +} +/* ------------------------------ net_listen ------------------------------ */ + + +NSAPI_PUBLIC int net_listen(SYS_NETFD s, int backlog) +{ + return PR_Listen(s, backlog)==PR_FAILURE?IO_ERROR:0; +} + + +/* ------------------------- net_create_listener -------------------------- */ + + +NSAPI_PUBLIC SYS_NETFD net_create_listener(char *ipstr, int port) +{ + SYS_NETFD sd; + /* + struct sockaddr_in sa_server; + */ + PRNetAddr sa_server; + PRStatus status; + PRInt32 flags; + + if ((sd = net_socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == SYS_NET_ERRORFD) { + return SYS_NET_ERRORFD; + } + +#ifdef SOLARIS + /* + * unset NONBLOCK flag; + */ + /* Have no idea why Solaris want to unset NONBLOCK flag when it should + be in NON-BLOCK mode, and new NSPR20 does not give file descriptor + back, so the code are removed --- yjh + flags = fcntl(sd->osfd, F_GETFL, 0); + fcntl(sd->osfd, F_SETFL, flags & ~O_NONBLOCK); + */ +#endif + /* Convert to NSPR21 for ns security integration + ZERO((char *) &sa_server, sizeof(sa_server)); + sa_server.sin_family=AF_INET; + sa_server.sin_addr.s_addr = (ipstr ? inet_addr(ipstr) : htonl(INADDR_ANY)); + sa_server.sin_port=htons(port); + if(net_bind(sd, (struct sockaddr *) &sa_server,sizeof(sa_server)) < 0) { + return SYS_NET_ERRORFD; + } + net_listen(sd, net_listenqsize); + */ + + if (ipstr) { + status = PR_InitializeNetAddr(PR_IpAddrNull, port, &sa_server); + if (status == PR_SUCCESS) sa_server.inet.ip = inet_addr(ipstr); + else return SYS_NET_ERRORFD; + } + else { + status = PR_InitializeNetAddr(PR_IpAddrAny, port, &sa_server); + if (status == PR_FAILURE) return SYS_NET_ERRORFD; + } + + status = PR_Bind(sd, &sa_server); + if (status == PR_FAILURE) return SYS_NET_ERRORFD; + + + status = PR_Listen(sd, net_listenqsize); + if (status == PR_FAILURE) return SYS_NET_ERRORFD; + + return sd; +} +/* ------------------------------ net_select ------------------------------ */ + +/* +NSAPI_PUBLIC int net_select(int nfds, fd_set *r, fd_set *w, fd_set *e, + struct timeval *timeout) +{ + PR_fd_set rd, wr, ex; + int index; + int rv; + + if (nfds > (64*1024)) + return -1; + + PR_FD_ZERO(&rd); + PR_FD_ZERO(&wr); + PR_FD_ZERO(&ex); + + for (index=0; index<nfds; index++) { + if (FD_ISSET(index, r)) + PR_FD_NSET(index, &rd); + if (FD_ISSET(index, w)) + PR_FD_NSET(index, &wr); + if (FD_ISSET(index, e)) + PR_FD_NSET(index, &ex); + } + + rv = PR_Select(0, &rd, &wr, &ex, PR_SecondsToInterval(timeout->tv_sec)); + if (rv > 0) { + FD_ZERO(r); + FD_ZERO(w); + FD_ZERO(e); + for (index=0; index<nfds; index++) { + if (PR_FD_NISSET(index, &rd)) + FD_SET(index, r); + if (PR_FD_NISSET(index, &wr)) + FD_SET(index, w); + if (PR_FD_NISSET(index, &ex)) + FD_SET(index, e); + } + } + + return rv; +} +*/ + +NSAPI_PUBLIC int net_select(int nfds, fd_set *r, fd_set *w, fd_set *e, + struct timeval *timeout) +{ + return 1; +} + + +/* ----------------------------- net_isalive ------------------------------ */ + + +/* + * XXXmikep As suggested by shaver@ingenia.com. If everyone was POSIX + * compilent, a write() of 0 bytes would work as well + */ +NSAPI_PUBLIC int net_isalive(SYS_NETFD sd) +{ + char c; + if (PR_RecvFrom(sd, &c, 1, MSG_PEEK, NULL, 0) == -1 ) { + return 0; + } + return 1; +} + + +/* ------------------------------ net_connect ------------------------------ */ + +NSAPI_PUBLIC int net_connect(SYS_NETFD s, const void *sockaddr, int namelen) +{ + int rv; + + child_status(CHILD_WRITING); + rv = PR_Connect(s, (PRNetAddr *)sockaddr, PR_INTERVAL_NO_TIMEOUT); + child_status(CHILD_PROCESSING); + + return rv==PR_FAILURE?IO_ERROR:0; +} + + +/* ------------------------------ net_ioctl ------------------------------ */ + + +NSAPI_PUBLIC int net_ioctl(SYS_NETFD s, int tag, void *result) +{ +#if defined(NET_WINSOCK) + return ioctlsocket(PR_FileDesc2NativeHandle(s),tag,(unsigned long *)result); +#elif defined(XP_UNIX) + return ioctl(PR_FileDesc2NativeHandle(s), tag, result); +#else + write me; +#endif + +} +/* --------------------------- net_getpeername ---------------------------- */ + + +NSAPI_PUBLIC int net_getpeername(SYS_NETFD s, struct sockaddr *name, + int *namelen) +{ +#if defined (SNI) || defined (UnixWare) + return getpeername(PR_FileDesc2NativeHandle(s), name, (size_t *)namelen); +#else /* defined (SNI) || defined (UnixWare) */ + return getpeername(PR_FileDesc2NativeHandle(s), name, (TCPLEN_T *)namelen); +#endif /* defined (SNI) || defined (UnixWare) */ +} + + +/* ------------------------------ net_close ------------------------------- */ + + +NSAPI_PUBLIC int net_close(SYS_NETFD s) +{ + return PR_Close(s)==PR_FAILURE?IO_ERROR:0; +} + +NSAPI_PUBLIC int net_shutdown(SYS_NETFD s, int how) +{ + switch (how) { + case 0: + return PR_Shutdown(s, PR_SHUTDOWN_RCV); + break; + case 1: + return PR_Shutdown(s, PR_SHUTDOWN_SEND); + break; + case 2: + return PR_Shutdown(s, PR_SHUTDOWN_BOTH); + break; + default: + return -1; + } + + return 0; +} + + + +/* ------------------------------- net_bind ------------------------------- */ + +NSAPI_PUBLIC int net_bind(SYS_NETFD s, const struct sockaddr *name, + int namelen) +{ + return PR_Bind(s, (const PRNetAddr *)name)==PR_FAILURE?IO_ERROR:0; +} + + +/* ------------------------------ net_accept ------------------------------ */ + + +NSAPI_PUBLIC SYS_NETFD net_accept(SYS_NETFD sd, struct sockaddr *addr, + int *addrlen) +{ + SYS_NETFD sock = PR_Accept(sd, (PRNetAddr *)addr, PR_INTERVAL_NO_TIMEOUT); + + if (sock == NULL) + return SYS_NET_ERRORFD; + return sock; +} + +/* ------------------------------- net_read ------------------------------- */ + +NSAPI_PUBLIC int net_read(SYS_NETFD fd, char *buf, int sz, int timeout) +{ + int rv; + + if (timeout == NET_ZERO_TIMEOUT) + timeout = PR_INTERVAL_NO_WAIT; + else if (timeout == NET_INFINITE_TIMEOUT) + timeout = PR_INTERVAL_NO_TIMEOUT; + else + timeout = PR_SecondsToInterval(timeout); + + child_status(CHILD_READING); + rv = PR_Recv(fd, buf, sz, 0, timeout); + + child_status(CHILD_PROCESSING); + return rv; +} + + +/* ------------------------------ net_write ------------------------------- */ + +#ifndef NEEDS_WRITEV +int net_writev(SYS_NETFD fd, struct iovec *iov, int iov_size) +{ + int rv; + + child_status(CHILD_WRITING); + rv = PR_Writev(fd, (PRIOVec *)iov, iov_size, PR_INTERVAL_NO_TIMEOUT); + child_status(CHILD_PROCESSING); + return rv; +} + +#else /* NEEDS_WRITEV */ + +/* Since SSL and NT do not support writev(), we just emulate it. + * This does not lead to the optimal number of packets going out... + */ +int net_writev(SYS_NETFD fd, struct iovec *iov, int iov_size) +{ + int index; + + child_status(CHILD_WRITING); + + for (index=0; index<iov_size; index++) { + + /* net_write already does the buffer for nonblocked IO */ + if ( net_write(fd, iov[index].iov_base, iov[index].iov_len) ==IO_ERROR){ + child_status(CHILD_PROCESSING); + return IO_ERROR; + } + } + + child_status(CHILD_PROCESSING); + return IO_OKAY; +} +#endif /* NEEDS_WRITEV */ + + +NSAPI_PUBLIC int net_write(SYS_NETFD fd, char *buf, int sz) +{ + int rv; + + child_status(CHILD_WRITING); + rv = PR_Send(fd, buf, sz, 0, PR_INTERVAL_NO_TIMEOUT); + child_status(CHILD_PROCESSING); + if(rv < 0) { + return IO_ERROR; + } + return rv; +} + +NSAPI_PUBLIC int net_socketpair(SYS_NETFD *pair) +{ + return PR_NewTCPSocketPair(pair); +} + +#ifdef XP_UNIX +NSAPI_PUBLIC SYS_NETFD net_dup2(SYS_NETFD prfd, int osfd) +{ + SYS_NETFD newfd = NULL; + + if (prfd && PR_FileDesc2NativeHandle(prfd) != osfd) { + if (dup2(PR_FileDesc2NativeHandle(prfd), osfd) != -1) { + newfd = PR_ImportFile(osfd); + if (!newfd) + close(osfd); + } + } + + return newfd; +} + +NSAPI_PUBLIC int net_is_STDOUT(SYS_NETFD prfd) +{ + int fd = PR_FileDesc2NativeHandle(prfd); + if (fd == STDOUT_FILENO) return 1; + return 0; +} + +NSAPI_PUBLIC int net_is_STDIN(SYS_NETFD prfd) +{ + int fd = PR_FileDesc2NativeHandle(prfd); + if (fd == STDIN_FILENO) return 1; + return 0; +} + +#endif /* XP_UNIX */ + +/* -------------------------- Accept mutex crap --------------------------- */ + + +#ifndef NET_WINSOCK + + +#include "sem.h" +static SEMAPHORE mob_sem; +static int have_mob_sem; + + +void net_accept_enter(void) +{ + if(sem_grab(mob_sem) == -1) + ereport(LOG_CATASTROPHE, "sem_grab failed (%s)", system_errmsg()); + have_mob_sem = 1; +} + +int net_accept_tenter(void) +{ + int ret = sem_tgrab(mob_sem); + if(ret != -1) + have_mob_sem = 1; + return ret; +} + +void net_accept_exit(void) +{ + if(sem_release(mob_sem) == -1) + ereport(LOG_CATASTROPHE, "sem_release failed (%s)", system_errmsg()); + have_mob_sem = 0; +} + +#ifdef AIX +#undef accept +#define accept naccept +#endif + +void net_accept_texit(void) +{ + if(have_mob_sem && (sem_release(mob_sem) == -1)) + ereport(LOG_CATASTROPHE, "sem_release failed (%s)", system_errmsg()); + have_mob_sem = 0; +} + +int net_accept_init(int port) +{ + /* XXXMB how to translate this to nspr? */ + /* since SSL_AcceptHook is no longer in ns security (HCL_1_5), + so this is gone! (It does exist in HCL_101) + SSL_AcceptHook((SSLAcceptFunc)PR_Accept); + */ + have_mob_sem = 0; + mob_sem = sem_init("netsite", port); + return (mob_sem == SEM_ERROR ? -1 : 0); +} + +void net_accept_terminate(void) +{ + sem_terminate(mob_sem); +} + +#endif /* !NET_WINSOCK */ + + +/* ----------------------------- net_ip2host ------------------------------ */ + + +char *dns_ip2host(char *ip, int verify); + +NSAPI_PUBLIC char *net_ip2host(char *ip, int verify) +{ + if(!net_enabledns) + return NULL; + + return dns_ip2host(ip, verify); +} + + + +/* ---------------------------- util_hostname ----------------------------- */ + + + +#ifdef XP_UNIX +#include <sys/param.h> +#else /* WIN32 */ +#define MAXHOSTNAMELEN 255 +#endif /* XP_UNIX */ + +/* Defined in dns.c */ +char *net_find_fqdn(PRHostEnt *p); + +NSAPI_PUBLIC char *util_hostname(void) +{ + char str[MAXHOSTNAMELEN]; + PRHostEnt hent; + char buf[PR_NETDB_BUF_SIZE]; + PRStatus err; + + gethostname(str, MAXHOSTNAMELEN); + err = PR_GetHostByName( + str, + buf, + PR_NETDB_BUF_SIZE, + &hent); + + if (err == PR_FAILURE) + return NULL; + return net_find_fqdn(&hent); +} + + diff --git a/lib/base/nscperror.c b/lib/base/nscperror.c new file mode 100644 index 00000000..9d791f89 --- /dev/null +++ b/lib/base/nscperror.c @@ -0,0 +1,162 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* nscperrors.c + * Very crude error handling for nspr and libsec. + */ + +#include "netsite.h" + +#define NSCP_NSPR_ERROR_BASE (-6000) +#define NSCP_NSPR_MAX_ERROR (NSCP_NSPR_ERROR_BASE + 29) +#define NSCP_LIBSEC_ERROR_BASE (-8192) +#define NSCP_LIBSEC_MAX_ERROR (NSCP_LIBSEC_ERROR_BASE + 63) +#define NSCP_LIBSSL_ERROR_BASE (-12288) +#define NSCP_LIBSSL_MAX_ERROR (NSCP_LIBSSL_ERROR_BASE + 19) + +typedef struct nscp_error_t { + int errorNumber; + const char *errorString; +} nscp_error_t; + +nscp_error_t nscp_nspr_errors[] = { + { 0, "NSPR error" }, + { 1, "Out of memory" }, + { 2, "Bad file descriptor" }, + { 3, "Data temporarily not available" }, + { 4, "Access fault" }, + { 5, "Invalid method" }, + { 6, "Illegal access" }, + { 7, "Unknown error" }, + { 8, "Pending interrupt" }, + { 9, "Not implemented" }, + { 10, "IO error" }, + { 11, "IO timeout error" }, + { 12, "IO already pending error" }, + { 13, "Directory open error" }, + { 14, "Invalid Argument" }, + { 15, "Address not available" }, + { 16, "Address not supported" }, + { 17, "Already connected" }, + { 18, "Bad address" }, + { 19, "Address already in use" }, + { 20, "Connection refused" }, + { 21, "Network unreachable" }, + { 22, "Connection timed out" }, + { 23, "Not connected" }, + { 24, "Load library error" }, + { 25, "Unload library error" }, + { 26, "Find symbol error" }, + { 27, "Connection reset by peer" }, + { 28, "Range Error" }, + { 29, "File Not Found Error" } +}; + +nscp_error_t nscp_libsec_errors[] = { + { 0, "SEC_ERROR_IO" }, + { 1, "SEC_ERROR_LIBRARY_FAILURE" }, + { 2, "SEC_ERROR_BAD_DATA" }, + { 3, "SEC_ERROR_OUTPUT_LEN" }, + { 4, "SEC_ERROR_INPUT_LEN" }, + { 5, "SEC_ERROR_INVALID_ARGS" }, + { 6, "SEC_ERROR_INVALID_ALGORITHM" }, + { 7, "SEC_ERROR_INVALID_AVA" }, + { 8, "SEC_ERROR_INVALID_TIME" }, + { 9, "SEC_ERROR_BAD_DER" }, + { 10, "SEC_ERROR_BAD_SIGNATURE" }, + { 11, "SEC_ERROR_EXPIRED_CERTIFICATE" }, + { 12, "SEC_ERROR_REVOKED_CERTIFICATE" }, + { 13, "SEC_ERROR_UNKNOWN_ISSUER" }, + { 14, "SEC_ERROR_BAD_KEY" }, + { 15, "SEC_ERROR_BAD_PASSWORD" }, + { 16, "SEC_ERROR_UNUSED" }, + { 17, "SEC_ERROR_NO_NODELOCK" }, + { 18, "SEC_ERROR_BAD_DATABASE" }, + { 19, "SEC_ERROR_NO_MEMORY" }, + { 20, "SEC_ERROR_UNTRUSTED_ISSUER" }, + { 21, "SEC_ERROR_UNTRUSTED_CERT" }, + { 22, "SEC_ERROR_DUPLICATE_CERT" }, + { 23, "SEC_ERROR_DUPLICATE_CERT_TIME" }, + { 24, "SEC_ERROR_ADDING_CERT" }, + { 25, "SEC_ERROR_FILING_KEY" }, + { 26, "SEC_ERROR_NO_KEY" }, + { 27, "SEC_ERROR_CERT_VALID" }, + { 28, "SEC_ERROR_CERT_NOT_VALID" }, + { 29, "SEC_ERROR_CERT_NO_RESPONSE" }, + { 30, "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE" }, + { 31, "SEC_ERROR_CRL_EXPIRED" }, + { 32, "SEC_ERROR_CRL_BAD_SIGNATURE" }, + { 33, "SEC_ERROR_CRL_INVALID" }, + { 34, "SEC_ERROR_" }, + { 35, "SEC_ERROR_" }, + { 36, "SEC_ERROR_" }, + { 37, "SEC_ERROR_" }, + { 38, "SEC_ERROR_" }, + { 39, "SEC_ERROR_" }, + { 40, "SEC_ERROR_" }, + { 41, "SEC_ERROR_" }, + { 42, "SEC_ERROR_" }, + { 43, "SEC_ERROR_" }, + { 44, "SEC_ERROR_" }, + { 45, "SEC_ERROR_" }, + { 46, "SEC_ERROR_" }, + { 47, "SEC_ERROR_" }, + { 48, "SEC_ERROR_" }, + { 49, "SEC_ERROR_" }, + { 50, "SEC_ERROR_" }, + { 51, "SEC_ERROR_" }, + { 52, "SEC_ERROR_" }, + { 53, "SEC_ERROR_" }, + { 54, "SEC_ERROR_" }, + { 55, "SEC_ERROR_" }, + { 56, "SEC_ERROR_" }, + { 57, "SEC_ERROR_" }, + { 58, "SEC_ERROR_" }, + { 59, "SEC_ERROR_" }, + { 60, "SEC_ERROR_" }, + { 61, "SEC_ERROR_" }, + { 62, "SEC_ERROR_" }, + { 63, "SEC_ERROR_NEED_RANDOM" } +}; + +nscp_error_t nscp_libssl_errors[] = { + { 0, "SSL_ERROR_EXPORT_ONLY_SERVER" }, + { 1, "SSL_ERROR_US_ONLY_SERVER" }, + { 2, "SSL_ERROR_NO_CYPHER_OVERLAP" }, + { 3, "SSL_ERROR_NO_CERTIFICATE" }, + { 4, "SSL_ERROR_BAD_CERTIFICATE" }, + { 5, "unused SSL error #5" }, + { 6, "SSL_ERROR_BAD_CLIENT - the server has encountered bad data from the client." }, + { 7, "SSL_ERROR_BAD_SERVER" }, + { 8, "SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE" }, + { 9, "SSL_ERROR_UNSUPPORTED_VERSION" }, + { 10, "unused SSL error #10" }, + { 11, "SSL_ERROR_WRONG_CERTIFICATE" }, + { 12, "SSL_ERROR_BAD_CERT_DOMAIN" }, + { 13, "SSL_ERROR_POST_WARNING" }, + { 14, "SSL_ERROR_SSL2_DISABLED" }, + { 15, "SSL_ERROR_BAD_MAC_READ - SSL has received a record with an incorrect Message Authentication Code." }, + { 16, "SSL_ERROR_BAD_MAC_ALERT - SSL has received an error indicating an incorrect Message Authentication Code." }, + { 17, "SSL_ERROR_BAD_CERT_ALERT - the server cannot verify your certificate." }, + { 18, "SSL_ERROR_REVOKED_CERT_ALERT - the server has rejected your certificate as revoked." }, + { 19, "SSL_ERROR_EXPIRED_CERT_ALERT - the server has rejected your certificate as expired." }, +}; + +const char * +nscperror_lookup(int error) +{ + if ((error >= NSCP_NSPR_ERROR_BASE) && + (error <= NSCP_NSPR_MAX_ERROR)) { + return nscp_nspr_errors[error-NSCP_NSPR_ERROR_BASE].errorString; + } else if ((error >= NSCP_LIBSEC_ERROR_BASE) && + (error <= NSCP_LIBSEC_MAX_ERROR)) { + return nscp_libsec_errors[error-NSCP_LIBSEC_ERROR_BASE].errorString; + } else if ((error >= NSCP_LIBSSL_ERROR_BASE) && + (error <= NSCP_LIBSSL_MAX_ERROR)) { + return nscp_libssl_errors[error-NSCP_LIBSSL_ERROR_BASE].errorString; + } else { + return (const char *)NULL; + } +} diff --git a/lib/base/nterrors.cpp b/lib/base/nterrors.cpp new file mode 100644 index 00000000..2eaf29b3 --- /dev/null +++ b/lib/base/nterrors.cpp @@ -0,0 +1,83 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * nterrors.c: Conversion of error numbers to explanation strings + * + * Aruna Victor 12/6/95 + */ + + +#include <windows.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <netsite.h> +#include <base/nterrors.h>
+#include <base/nterr.h> + +struct _NtHashedError { + int ErrorNumber; + char *ErrorString; + struct _NtHashedError *next; +} ; + +typedef struct _NtHashedError NtHashedError; + +NtHashedError *hashedNtErrors[200]; + +#define HASH_ERROR_MODULUS 199 +#define DEFAULT_ERROR_STRING "Error Number is unknown" + +char * +FindError(int error) +{ + NtHashedError *tmp; + + int hashValue = error % HASH_ERROR_MODULUS; + tmp = hashedNtErrors[hashValue]; + + while(tmp) { + if (tmp->ErrorNumber == error) { + return tmp->ErrorString; + } + tmp = tmp->next; + } + return(DEFAULT_ERROR_STRING); +} + +void +EnterError(NtHashedError *error) +{ + NtHashedError *tmp; + int hashValue; + int number = 199; + + hashValue = error->ErrorNumber % HASH_ERROR_MODULUS; + + if(!(tmp = hashedNtErrors[hashValue])){ + hashedNtErrors[hashValue] = error; + } else { + while(tmp->next) { + tmp = tmp->next; + } + tmp->next = error; + } +} +
+void +HashNtErrors() +{ + NtHashedError *error; + int i = 0; + + while(NtErrorStrings[i].ErrorString) { + error = (NtHashedError *)MALLOC(sizeof(NtHashedError)); + error->ErrorNumber = NtErrorStrings[i].ErrorNumber; + error->ErrorString = NtErrorStrings[i++].ErrorString; + error->next = NULL; + EnterError(error); + } +}
\ No newline at end of file diff --git a/lib/base/plist.cpp b/lib/base/plist.cpp new file mode 100644 index 00000000..fe9d909e --- /dev/null +++ b/lib/base/plist.cpp @@ -0,0 +1,1154 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * MODULE: plist.c + * + * DESCRIPTION: + * + * This module implements property lists. A property list is an + * ordered array of property values. Each property value has an + * handle for some data item, and may have a reference to + * another property list which describes the type of the data + * item. Each property value has a property index which specifies + * its position in the property list. A property value may also + * have a name. Since the data item associated with a property + * value may reference another property list, it is possible to + * construct arbitrary linked structures of property lists. + * + * IMPLEMENTATION NOTES: + */ + +#include "netsite.h" +#include "base/plist.h" +#include "plist_pvt.h" + +int plistHashSizes[] = PLSTSIZES; + +/* + * FUNCTION: PListAssignValue + * + * DESCRIPTION: + * + * This function sets the value and/or type of a defined property + * in given property list. If the property type is specified as + * NULL, it is unchanged. However, the property value is always + * set to the specified value. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pname - the property name + * pvalue - the new property value + * ptype - the new property type, or NULL + * + * RETURNS: + * + * If successful, the property index of the referenced property is + * returned as the function value. Errors are indicated by a + * negative return code as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListAssignValue(PList_t plist, const char *pname, + const void *pvalue, PList_t ptype) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t *pv; + int pindex; + int i; + + if (!plist) return ERRPLUNDEF; + + /* Got a symbol table for this property list? */ + if (pl->pl_symtab) { + + /* Yes, compute hash of specified name */ + i = PListHashName(pl->pl_symtab, pname); + + /* Search hash collision list for matching name */ + for (pv = pl->pl_symtab->pt_hash[i]; pv; pv = pv->pv_next) { + + if (!strcmp(pname, pv->pv_name)) { + + /* Name match, get property index */ + pindex = pv->pv_pi; + + /* Set the new value */ + pv->pv_value = (char *)pvalue; + + /* Set type if type is given */ + if (ptype) pv->pv_type = (PListStruct_t *)ptype; + + /* Return the property index */ + return pindex; + } + } + } + + /* Error - specified property name is undefined */ + return ERRPLUNDEF; +} + +/* + * FUNCTION: PListCreate + * + * DESCRIPTION: + * + * This function creates a new property list and returns a handle for + * it. It allows the caller to reserve a specified number of + * property indices at the beginning of the list, and also to limit + * the total number of property values that may be added to the list. + * + * ARGUMENTS: + * + * mempool - handle for a memory pool to be associated + * with the new property list + * resvprop - number of reserved property indices + * maxprop - maximum number of properties in list + * (zero or negative imposes no limit) + * flags - unused, reserved, must be zero + * + * RETURNS: + * + * If successful, the function return value is a handle for the new + * property list. Otherwise NULL is returned. + */ + +NSAPI_PUBLIC PList_t +PListCreate(pool_handle_t *mempool, int resvprop, int maxprop, int flags) +{ + PListStruct_t *plist; /* pointer to property list structure */ + int i; + + plist = (PListStruct_t *)pool_malloc(mempool, sizeof(PListStruct_t)); + if (plist) { + + /* Negative maxprop is the same as zero, i.e. no limit */ + if (maxprop < 0) maxprop = 0; + + /* If resvprop and maxprop are both specified, limit resvprop */ + if (resvprop > 0) { + if (maxprop && (resvprop > maxprop)) resvprop = maxprop; + } + else resvprop = 0; + + /* Initialize property list structure */ + plist->pl_mempool = mempool; + plist->pl_symtab = NULL; + plist->pl_maxprop = maxprop; + plist->pl_resvpi = resvprop; + plist->pl_initpi = resvprop; + plist->pl_lastpi = resvprop; + + /* Set initialize size for array of property value pointers */ + plist->pl_cursize = (resvprop) ? resvprop : PLIST_DEFSIZE; + + /* Allocate the initial array of property value pointers */ + plist->pl_ppval = (pb_entry **)pool_malloc(mempool, + (plist->pl_cursize * + sizeof(PLValueStruct_t *))); + if (!plist->pl_ppval) { + + /* Failed - insufficient memory */ + pool_free(mempool, (void *)plist); + plist = NULL; + } + else { + /* NULL out pointers in the reserved index range, if any */ + for (i = 0; i < plist->pl_lastpi; ++i) { + plist->pl_ppval[i] = 0; + } + } + } + + return (PList_t)plist; +} + +/* + * FUNCTION: PListDefProp + * + * DESCRIPTION: + * + * This function creates a new property in a specified property list. + * The 'pindex' argument may be used to request a particular property + * index for the new property. If 'pindex' is greater than zero, + * the specified value is used as the new property's index, provided + * there is no property at that index already. If 'pindex' is zero, + * then the next available property index is assigned to the new + * property. A name may optionally be specified for the new property. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - new property index (or zero) + * pname - new property name (or NULL) + * + * RETURNS: + * + * If successful, the index of the new property is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListDefProp(PList_t plist, int pindex, const char *pname, const int flags) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + int cursize; + int i; + int wrapped; + + if (!plist) return ERRPLUNDEF; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Is pindex specified? */ + if (pindex > 0) { + + /* Yes, is it in the reserved range? */ + if (flags != PLFLG_IGN_RES && pindex > pl->pl_resvpi) { + /* No, error */ + return ERRPLINVPI; + } + + i = pindex - 1; + + if (ppval[i]) { + /* Error - property already exists at specified index */ + return ERRPLEXIST; + } + } + else { + + /* + * Look for a free property index, starting at pl_lastpi + 1. + * (Note that i is the property index - 1) + */ + for (wrapped = 0, i = pl->pl_lastpi; ;) { + + /* Are we in an initialized part of the array? */ + if (i < pl->pl_initpi) { + + /* Yes, use this index if it's free */ + if (ppval[i] == 0) break; + + /* Otherwise step to the next one */ + ++i; + } + else { + + /* Have we reached the end yet? */ + if (i < pl->pl_cursize) { + + /* + * We are above the highest initialized index, but + * still within the allocated size. An index in + * this range can be used with no further checks. + */ + ppval[i] = 0; + } + else { + + /* + * It's looking like time to grow the array, but + * first go back and look for an unused, unreserved + * index that might have been freed. + */ + if (!wrapped) { + + i = pl->pl_resvpi; + wrapped = 1; + continue; + } + + /* + * Grow the array unless there is a specified maximum + * size and we've reached it. + */ + i = pl->pl_cursize; + if (pl->pl_maxprop && (i > pl->pl_maxprop)) { + + /* Error - property list is full */ + return ERRPLFULL; + } + + /* Increase planned size of list */ + cursize = i + PLIST_DEFGROW; + + /* Reallocate the array of property value pointers */ + ppval = (PLValueStruct_t **)pool_realloc(pl->pl_mempool, + (void *)ppval, + (cursize * sizeof(PLValueStruct_t *))); + if (!ppval) { + + /* Error - insufficient memory */ + return ERRPLNOMEM; + } + + /* Initialize the first new entry and select it */ + ppval[i] = NULL; + pl->pl_ppval = (pb_entry **)ppval; + pl->pl_cursize = cursize; + } + + /* Update the highest initialized index value */ + pl->pl_initpi = i + 1; + break; + } + } + + /* Set the starting point for the next allocation */ + pl->pl_lastpi = i + 1; + } + + /* We have a property index (i + 1). Create a new property value */ + pv = (PLValueStruct_t *)pool_calloc(pl->pl_mempool, + 1, sizeof(PLValueStruct_t)); + if (!pv) { + + /* Error - insufficient memory */ + return ERRPLNOMEM; + } + + pv->pv_pbentry.param = &pv->pv_pbparam; + pv->pv_pi = i + 1; + ppval[i] = pv; + + /* Name the property if the name was specified */ + if (pname) { + + /* XXX Maybe should delete property if naming fails */ + return PListNameProp(plist, i + 1, pname); + } + + /* Return the property index of the new property */ + return i + 1; +} + +/* + * FUNCTION: PListDeleteProp + * + * DESCRIPTION: + * + * This function deletes a property from a specified property list. + * The property can be specified by its property index, using a + * pindex value greater than zero, or by its name, by specifying + * pindex as zero and pname as the property name. This does not + * have any effect on the data referenced by the property value, + * if any, nor does it have any effect on the property list that + * describes the property value's type, if any. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - the property index, or zero + * pname - the property name, or NULL + */ + +NSAPI_PUBLIC const void * +PListDeleteProp(PList_t plist, int pindex, const char *pname_in) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t **pvp; + PLValueStruct_t *pv = NULL; + int i; + const void *pvalue = NULL; + char *pname = (char *)pname_in; + + if (!plist) return NULL; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Check for valid property index */ + if ((pindex > 0) && (pindex <= pl->pl_initpi)) { + + /* Get the pointer to the property structure */ + pv = ppval[pindex - 1]; + pname = 0; + if (pv) { + pname = pv->pv_name; + } + } + + if (pname && pl->pl_symtab) { + + /* Compute hash of specified property name */ + i = PListHashName(pl->pl_symtab, pname); + + /* Search hash collision list for matching name */ + for (pvp = &pl->pl_symtab->pt_hash[i]; *pvp; pvp = &(*pvp)->pv_next) { + + pv = *pvp; + if (!strcmp(pname, pv->pv_name)) { + + /* Found it. Get its index and remove it. */ + pindex = pv->pv_pi; + *pvp = pv->pv_next; + break; + } + } + } + + /* Found the indicated property by index or name? */ + if (pv) { + + /* Yes, remove it from the property list */ + ppval[pindex - 1] = NULL; + + /* Free the property name, if any */ + if (pv->pv_name) { + pool_free(pl->pl_mempool, (void *)(pv->pv_name)); + } + pvalue = pv->pv_value; + + /* Free the property */ + pool_free(pl->pl_mempool, (void *)pv); + } + return(pvalue); +} + +/* + * FUNCTION: PListFindValue + * + * DESCRIPTION: + * + * This function retrieves the value and type of a property with a + * specified property name. If the pvalue argument is non-NULL, + * it specifies a location in which to return the property value. + * Similarly, if ptype is non-NULL, it specifies where the property + * list describing the property type is to be returned. If a + * property has no value, the value returned for pvalue is NULL. + * If a property has no type, the value returned for ptype is NULL. + * A property can have a value, a type, both, or neither. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pname - pointer to property name string + * pvalue - property value return pointer + * ptype - property type return pointer + * + * RETURNS: + * + * If successful, the index of the referenced property is returned + * as the function value. Errors are indicated by a negative + * return code as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListFindValue(PList_t plist, const char *pname, void **pvalue, PList_t *ptype) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t *pv; + int pindex; + int i; + + if (!plist) return ERRPLUNDEF; + + /* Got a symbol table for this property list? */ + if (pl->pl_symtab) { + + /* Yes, compute hash of specified name */ + i = PListHashName(pl->pl_symtab, pname); + + /* Search hash collision list for matching name */ + for (pv = pl->pl_symtab->pt_hash[i]; pv; pv = pv->pv_next) { + + if (!strcmp(pname, pv->pv_name)) { + + /* Name match, get property index */ + pindex = pv->pv_pi; + + /* Return the value if requested */ + if (pvalue) *pvalue = (void *)(pv->pv_value); + + /* Return the type if requested */ + if (ptype) *ptype = (PList_t)(pv->pv_type); + + /* Return the property index */ + return pindex; + } + } + } + + /* Error - specified property name is undefined */ + return ERRPLUNDEF; +} + +/* + * FUNCTION: PListInitProp + * + * DESCRIPTION: + * + * This function combines the functions of PListDefProp() and + * PListSetValue(), defining a new property and assigning it an + * initial value and optionally a type and/or a name. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - a reserved property index, or zero + * pname - the new property name, or NULL + * pvalue - the new property value + * ptype - the new property type, or NULL + * + * RETURNS: + * + * If successful, the property index (pindex) is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListInitProp(PList_t plist, int pindex, const char *pname, + const void *pvalue, PList_t ptype) +{ + int rv; + + if (!plist) return ERRPLUNDEF; + + /* Create the property */ + rv = PListDefProp(plist, pindex, pname, PLFLG_USE_RES); + if (rv > 0) { + + /* If that worked, set the value and type */ + rv = PListSetValue(plist, rv, pvalue, ptype); + } + + return rv; +} + +/* + * FUNCTION: PListNew + * + * DESCRIPTION: + * + * This function creates a new property list, using the specified + * memory pool for allocating the internal data structures used to + * represent it. If the mempool argument is NULL, the default + * memory pool is used. + * + * ARGUMENTS: + * + * mempool - handle for a memory pool to be associated + * with the new property list + * + * RETURNS: + * + * If successful, the function return value is a handle for the new + * property list. Otherwise NULL is returned. + */ + +NSAPI_PUBLIC PList_t +PListNew(pool_handle_t *mempool) +{ + /* Just call PListCreate with default parameters */ + return PListCreate(mempool, 0, 0, 0); +} + +/* + * FUNCTION: PListDestroy + * + * DESCRIPTION: + * + * This function destroys a specified property list. This means + * that any dynamic memory which was allocated as a result of calls + * to the property list API is freed to the memory pool from which + * it was allocated. Property value data is not freed, nor are + * any property lists associated with property types. + * + * ARGUMENTS: + * + * plist - handle for the property list + */ + +void +PListDestroy(PList_t plist) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + int i; + + if (!plist) return; + + /* Free the property name symbol table if any */ + if (pl->pl_symtab) { + pool_free(pl->pl_mempool, (void *)(pl->pl_symtab)); + } + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Loop over the initialized property indices */ + for (i = 0; i < pl->pl_initpi; ++i) { + + /* Got a property here? */ + pv = ppval[i]; + if (pv) { + + /* Free the property name string if any */ + if (pv->pv_name) { + pool_free(pl->pl_mempool, (void *)(pv->pv_name)); + } + + /* Free the property value structure */ + pool_free(pl->pl_mempool, (void *)pv); + } + } + + /* Free the array of pointers to property values */ + pool_free(pl->pl_mempool, (void *)ppval); + + /* Free the property list head */ + pool_free(pl->pl_mempool, (void *)pl); +} + +/* + * FUNCTION: PListGetValue + * + * DESCRIPTION: + * + * This function retrieves the value and type of the property with + * the property index given by pindex in the specified property + * list. The pindex argument must specify the index of a defined + * property. If the pvalue argument is non-NULL, it specifies a + * location in which to return the property value. Similarly, if + * ptype is non-NULL, it specifies where the property list + * describing the property type is to be returned. If a property + * has no value, the value returned for pvalue is NULL. If a + * property has no type, the value returned for ptype is NULL. A + * property can have a value, a type, both, or neither. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - the property index + * pvalue - property value return pointer + * ptype - property type return pointer + * + * RETURNS: + * + * If successful, the property index (pindex) is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListGetValue(PList_t plist, int pindex, void **pvalue, PList_t *ptype) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + + if (!plist) return ERRPLUNDEF; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Check for valid property index */ + if ((pindex > 0) && (pindex <= pl->pl_initpi)) { + + /* Does the property exist? */ + pv = ppval[pindex - 1]; + if (pv) { + + /* Yes, return the value if requested */ + if (pvalue) *pvalue = (void *)(pv->pv_value); + + /* Return the type if requested */ + if (ptype) *ptype = (PList_t)(pv->pv_type); + + /* Successful return */ + return pindex; + } + } + + /* Error - invalid property index or non-existent property */ + return ERRPLINVPI; +} + +/* + * FUNCTION: PListHashName + * + * DESCRIPTION: + * + * This function hashes a given property name for a specified + * symbol table. It produces a value that can be used as an + * index in the pt_hash array associated with the symbol table. + * + * ARGUMENTS: + * + * symtab - pointer to the symbol table + * pname - pointer to the property name string + * + * RETURNS: + * + * The hash index is returned as the function value. + */ + +int +PListHashName(PLSymbolTable_t *symtab, const char *pname) +{ + unsigned int hashval = 0; /* hash value */ + + while (*pname) { + hashval = (hashval<<5) ^ (*pname++ & 0x7f); + } + + return hashval % PLSIZENDX(symtab->pt_sizendx); +} + +/* + * FUNCTION: PListNameProp + * + * DESCRIPTION: + * + * This function assigns a name to a defined property with the + * property index, pindex. If the property has an existing name, + * it will be replaced with the name specified by pname. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - the property index + * pname - the new property name + * + * RETURNS: + * + * If successful, the property index (pindex) is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListNameProp(PList_t plist, int pindex, const char *pname) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t *pv; + PLSymbolTable_t *pt; + int i; + + if (!plist) return ERRPLUNDEF; + + pt = pl->pl_symtab; + + /* Check for valid property index */ + if ((pindex > 0) && (pindex <= pl->pl_initpi)) { + + /* Does the property exist? */ + pv = ((PLValueStruct_t **)(pl->pl_ppval))[pindex - 1]; + if (pv) { + + /* If it has a name already, unname it */ + if (pv->pv_name) { + PLValueStruct_t **pvp; + + /* Get hash bucket index */ + i = PListHashName(pt, pv->pv_name); + + /* Seach hash collision list for this property */ + for (pvp = &pt->pt_hash[i]; + *pvp; pvp = &(*pvp)->pv_next) { + + if (*pvp == pv) { + + /* Remove it from the list */ + *pvp = pv->pv_next; + break; + } + } + + /* Free the current name string */ + pool_free(pl->pl_mempool, (void *)(pv->pv_name)); + } + + /* Got a new name? */ + if (pname) { + + /* Yes, is there a hash table? */ + if (!pt) { + + /* No, create one */ + pt = (PLSymbolTable_t *)pool_calloc(pl->pl_mempool, 1, + PLHASHSIZE(0)); + if (!pt) { + return ERRPLNOMEM; + } + + pl->pl_symtab = pt; + } + else { + + /* Is it time to grow the hash table? */ + i = PLSIZENDX(pt->pt_sizendx); + if ((pt->pt_sizendx < PLMAXSIZENDX) && + pt->pt_nsyms >= (i + i)) { + + PLSymbolTable_t *npt; + + /* Yes, allocate the new table */ + npt = (PLSymbolTable_t *)pool_calloc(pl->pl_mempool, 1, + PLHASHSIZE(pt->pt_sizendx+1)); + if (npt) { + PLValueStruct_t *opv; + PLValueStruct_t *npv; + int j; + + npt->pt_sizendx = pt->pt_sizendx + 1; + npt->pt_nsyms = pt->pt_nsyms; + + /* Rehash all the names into the new table */ + for (i = 0; i < PLSIZENDX(pt->pt_sizendx); ++i) { + for (opv = pt->pt_hash[i]; opv; opv = npv) { + npv = opv->pv_next; + j = PListHashName(npt, opv->pv_name); + opv->pv_next = npt->pt_hash[j]; + npt->pt_hash[j] = opv; + } + } + + pl->pl_symtab = npt; + + /* Free the old symbol table */ + pool_free(pl->pl_mempool, (void *)pt); + pt = npt; + } + } + } + + /* Duplicate the name string */ + pv->pv_name = pool_strdup(pl->pl_mempool, (char *)pname); + + /* Add name to symbol table */ + i = PListHashName(pt, pname); + pv->pv_next = pt->pt_hash[i]; + pt->pt_hash[i] = pv; + } + + /* Successful return */ + return pindex; + } + } + + /* Error - invalid property index or non-existent property */ + return ERRPLINVPI; +} + +/* + * FUNCTION: PListSetType + * + * DESCRIPTION: + * + * This function sets the property type of the defined property + * with the property index, pindex. The property list describing + * the property type is specified by ptype. If ptype is NULL, + * the property type will be set to be undefined (NULL). + * + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - the property index + * ptype - the new property type, or NULL + * + * RETURNS: + * + * If successful, the property index (pindex) is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListSetType(PList_t plist, int pindex, PList_t ptype) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + + if (!plist) return ERRPLUNDEF; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Check for valid property index */ + if ((pindex > 0) && (pindex <= pl->pl_initpi)) { + + /* Does the property exist? */ + pv = ppval[pindex - 1]; + if (pv) { + + /* Yes, set the new type */ + pv->pv_type = ptype; + + /* Successful return */ + return pindex; + } + } + + /* Error - invalid property index or non-existent property */ + return ERRPLINVPI; +} + +/* + * FUNCTION: PListSetValue + * + * DESCRIPTION: + * + * This function sets the value and optionally the type of a + * defined property in a given property list. The pindex argument + * specifies the property index, which must be greater than zero. + * The ptype argument specifies a property list that describes the + * property type. If ptype is NULL, the property type, if any, is + * unchanged by this function. However, the property value is + * always set to the value given by pvalue. + * + * ARGUMENTS: + * + * plist - handle for the property list + * pindex - the property index + * pvalue - the new property value + * ptype - the new property type, or NULL + * + * RETURNS: + * + * If successful, the property index (pindex) is returned as the + * function value. Errors are indicated by a negative return code + * as defined in plist.h. + */ + +NSAPI_PUBLIC int +PListSetValue(PList_t plist, int pindex, const void *pvalue, PList_t ptype) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + + if (!plist) return ERRPLUNDEF; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Check for valid property index */ + if ((pindex > 0) && (pindex <= pl->pl_initpi)) { + + /* Does the property exist? */ + pv = ppval[pindex - 1]; + if (pv) { + + /* Yes, set the new value */ + pv->pv_value = (char *)pvalue; + + /* Set type if type is given */ + if (ptype) pv->pv_type = (PListStruct_t *)ptype; + + /* Successful return */ + return pindex; + } + } + + /* Error - invalid property index or non-existent property */ + return ERRPLINVPI; +} + +/* + * FUNCTION: PListEnumerate + * + * DESCRIPTION: + * + * This function walks through a specified property list + * calling a user supplied function with the property + * name and value as parameters. + * + * ARGUMENTS: + * + * plist - handle for the property list + * user_func - handle for the user function + */ + +NSAPI_PUBLIC void +PListEnumerate(PList_t plist, PListFunc_t *user_func, void *user_data) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + int i; + + if (!plist) return; + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Loop over the initialized property indices */ + for (i = 0; i < pl->pl_initpi; ++i) { + + /* Got a property here? */ + pv = ppval[i]; + if (pv) { + (*user_func)(pv->pv_name, pv->pv_value, user_data); + } + + } + +} + +/* + * FUNCTION: PListCreateDuplicate + * + * DESCRIPTION: + * + * This function creates a new property list and returns a handle for + * it. The source plist provides the new plists parameters. + * + * ARGUMENTS: + * + * src_plist - source plist to duplicate + * mempool - handle for a memory pool to be associated + * with the new property list, only + * used if flags is set to PLFLG_NEW_MPOOL + * flags - if PLFLG_NEW_MPOOL uses new_mempool + * parameter + * + * RETURNS: + * + * If successful, the function return value is a handle for the new + * property list. Otherwise NULL is returned. + */ + +static PList_t +PListCreateDuplicate(PList_t src_plist, pool_handle_t *new_mempool, int flags) +{ + PListStruct_t *plist; /* pointer to property list structure */ + int i; + pool_handle_t *mempool; + + mempool = (flags == PLFLG_NEW_MPOOL) ? new_mempool : src_plist->pl_mempool; + + plist = (PListStruct_t *)pool_malloc(mempool, sizeof(PListStruct_t)); + if (plist) { + + /* Initialize property list structure */ + plist->pl_mempool = mempool; + plist->pl_symtab = NULL; + plist->pl_maxprop = src_plist->pl_maxprop; + plist->pl_resvpi = src_plist->pl_resvpi; + plist->pl_initpi = src_plist->pl_initpi; + plist->pl_lastpi = src_plist->pl_lastpi; + + /* Set initialize size for array of property value pointers */ + plist->pl_cursize = src_plist->pl_cursize; + + /* Allocate the initial array of property value pointers */ + plist->pl_ppval = (pb_entry **)pool_malloc(mempool, + (plist->pl_cursize * + sizeof(PLValueStruct_t *))); + if (!plist->pl_ppval) { + + /* Failed - insufficient memory */ + pool_free(mempool, (void *)plist); + plist = NULL; + } + else { + /* NULL out pointers in the reserved index range, if any */ + for (i = 0; i < plist->pl_lastpi; ++i) { + plist->pl_ppval[i] = 0; + } + } + } + + return (PList_t)plist; +} + + +/* + * FUNCTION: PListDuplicate + * + * DESCRIPTION: + * + * This function duplicates a specified PList_t. + * + * ARGUMENTS: + * + * plist - handle for the property list + * mempool - handle for a memory pool to be associated + * with the new property list + * resvprop - number of reserved property indices + * maxprop - maximum number of properties in list + * (zero or negative imposes no limit) + * flags - unused, reserved, must be zero + * + * RETURNS: + * + * If successful, the function return value is a handle for the new + * property list. Otherwise NULL is returned. + */ + +NSAPI_PUBLIC PList_t +PListDuplicate(PList_t plist, pool_handle_t *new_mempool, int flags) +{ + PListStruct_t *pl = (PListStruct_t *)plist; + PLValueStruct_t **ppval; + PLValueStruct_t *pv; + int i; + int rv = 0; + PList_t new_plist; + + if (!plist) return NULL; + + new_plist = PListCreateDuplicate(plist, new_mempool, flags); + if (new_plist == NULL) { + return(NULL); + } + + ppval = (PLValueStruct_t **)(pl->pl_ppval); + + /* Loop over the initialized property indices */ + for (i = 0; i < pl->pl_initpi; ++i) { + + /* Got a property here? */ + pv = ppval[i]; + if (pv) { + /* Create the property */ + rv = PListDefProp(new_plist, i + 1, pv->pv_name, PLFLG_IGN_RES); + if (rv > 0) { + + /* If that worked, set the value and type */ + rv = PListSetValue(new_plist, rv, pv->pv_value, pv->pv_type); + } + + if ( rv <= 0 ) { + PListDestroy(new_plist); + return(NULL); + } + } + + } + + return(new_plist); +} + +/* + * FUNCTION: PListGetPool + * + * DESCRIPTION: + * + * This function returns the memory pool the PList is allocated from. + * + * ARGUMENTS: + * + * plist - handle for the property list + * + * RETURNS: + * + * The memory pool address, which can be NULL. + */ + +NSAPI_PUBLIC pool_handle_t * +PListGetPool(PList_t plist) +{ + if (!plist) return NULL; + + return(plist->pl_mempool); +} + diff --git a/lib/base/plist_pvt.h b/lib/base/plist_pvt.h new file mode 100644 index 00000000..e4b861c5 --- /dev/null +++ b/lib/base/plist_pvt.h @@ -0,0 +1,122 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _PLIST_PVT_H +#define _PLIST_PVT_H + +/* + * FILE: plist_pvt.h + * + * DESCRIPTION: + * + * This file contains private definitions for the property list + * utility implementation. + */ + +#include "base/pool.h" + +/* Forward declarations */ +typedef struct PLValueStruct_s PLValueStruct_t; +typedef struct PLSymbol_s PLSymbol_t; +typedef struct PLSymbolTable_s PLSymbolTable_t; +typedef struct PListStruct_s PListStruct_t; + +/* + * TYPE: PLValueStruct_t + * + * DESCRIPTION: + * + * This type represents a property value. It is dynamically + * allocated when a new property is added to a property list. + * It contains a reference to a property list that contains + * information about the property value, and a reference to + * the property value data. + */ + +#ifndef PBLOCK_H +#include "base/pblock.h" +#endif /* PBLOCK_H */ +#include <stddef.h> + +struct PLValueStruct_s { + pb_entry pv_pbentry; /* used for pblock compatibility */ + pb_param pv_pbparam; /* property name and value pointers */ + PLValueStruct_t *pv_next; /* property name hash collision link */ + PListStruct_t *pv_type; /* property value type reference */ + int pv_pi; /* property index */ + int pv_flags; /* bit flags */ +}; + +#define pv_name pv_pbparam.name +#define pv_value pv_pbparam.value + +/* pv_flags definitions */ +#define PVF_MALLOC 0x1 /* allocated via MALLOC */ + +/* Offset to pv_pbparam in PLValueStruct_t */ +#define PVPBOFFSET offsetof(struct PLValueStruct_s,pv_pbparam) + +/* Convert pb_param pointer to PLValueStruct_t pointer */ +#define PATOPV(p) ((PLValueStruct_t *)((char *)(p) - PVPBOFFSET)) + +/* + * TYPE: PLSymbolTable_t + * + * DESCRIPTION: + * + * This type represents a symbol table that maps property names + * to properties. It is dynamically allocated the first time a + * property is named. + */ + +#define PLSTSIZES {7, 19, 31, 67, 123, 257, 513} +#define PLMAXSIZENDX (sizeof(plistHashSizes)/sizeof(plistHashSizes[0])) + +struct PLSymbolTable_s { + int pt_sizendx; /* pt_hash size, as an index in PLSTSIZES */ + int pt_nsyms; /* number of symbols in table */ + PLValueStruct_t *pt_hash[1];/* variable-length array */ +}; + +/* + * TYPE: PListStruct_t + * + * DESCRIPTION: + * + * This type represents the top-level of a property list structure. + * It is dynamically allocated when a property list is created, and + * freed when the property list is destroyed. It references a + * dynamically allocated array of pointers to property value + * structures (PLValueStruct_t). + */ + +#define PLIST_DEFSIZE 8 /* default initial entries in pl_ppval */ +#define PLIST_DEFGROW 16 /* default incremental entries for pl_ppval */ + +struct PListStruct_s { + pblock pl_pb; /* pblock subset of property list head */ + PLSymbolTable_t *pl_symtab; /* property name to index symbol table */ + pool_handle_t *pl_mempool; /* associated memory pool handle */ + int pl_maxprop; /* maximum number of properties */ + int pl_resvpi; /* number of reserved property indices */ + int pl_lastpi; /* last allocated property index */ + int pl_cursize; /* current size of pl_ppval in entries */ +}; + +#define pl_initpi pl_pb.hsize /* number of pl_ppval entries initialized */ +#define pl_ppval pl_pb.ht /* pointer to array of value pointers */ + +/* Convert pblock pointer to PListStruct_t pointer */ +#define PBTOPL(p) ((PListStruct_t *)(p)) + +#define PLSIZENDX(i) (plistHashSizes[i]) +#define PLHASHSIZE(i) (sizeof(PLSymbolTable_t) + \ + (PLSIZENDX(i) - 1)*sizeof(PLValueStruct_t *)) + +extern int plistHashSizes[7]; + +extern int PListHashName(PLSymbolTable_t *symtab, const char *pname); + +#endif /* _PLIST_PVT_H */ diff --git a/lib/base/pool.cpp b/lib/base/pool.cpp new file mode 100644 index 00000000..56232e3f --- /dev/null +++ b/lib/base/pool.cpp @@ -0,0 +1,654 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Generic pool handling routines. + * + * Hopefully these reduce the number of malloc/free calls. + * + * + * Thread warning: + * This implementation is thread safe. However, simultaneous + * mallocs/frees to the same "pool" are not safe. If you wish to + * use this module across multiple threads, you should define + * POOL_LOCKING which will make the malloc pools safe. + * + * Mike Belshe + * 11-20-95 + * + */ + +#include "netsite.h" +#include "base/systems.h" +#include "base/systhr.h" + +#ifdef MALLOC_POOLS +#include "base/pool.h" +#include "base/ereport.h" +#include "base/session.h" +#include "frame/req.h" +#include "frame/http.h" +#include "base/util.h" +#include "base/crit.h" + +#include "base/dbtbase.h" + +#ifdef DEBUG +#define POOL_ZERO_DEBUG +#endif +#undef POOL_LOCKING + +#define BLOCK_SIZE (32 * 1024) +#define MAX_FREELIST_SIZE (BLOCK_SIZE * 32) + +/* WORD SIZE 8 sets us up for 8 byte alignment. */ +#define WORD_SIZE 8 +#undef ALIGN +#define ALIGN(x) ( (x + WORD_SIZE-1) & (~(WORD_SIZE-1)) ) + +/* block_t + * When the user allocates space, a BLOCK_SIZE (or larger) block is created in + * the pool. This block is used until all the space is eaten within it. + * When all the space is gone, a new block is created. + * + */ +typedef struct block_t { + char *data; /* the real alloc'd space */ + char *start; /* first free byte in block */ + char *end; /* ptr to end of block */ + struct block_t *next; /* ptr to next block */ +} block_t; + +/* pool_t + * A pool is a collection of blocks. The blocks consist of multiple + * allocations of memory, but a single allocation cannot be freed by + * itself. Once the memory is allocated it is allocated until the + * entire pool is freed. + */ +typedef struct pool_t { +#ifdef DEBUG_CACHES + time_t time_created; +#endif +#ifdef POOL_LOCKING + CRITICAL lock; /* lock for modifying the pool */ +#endif + block_t *curr_block; /* current block being used */ + block_t *used_blocks; /* blocks that are all used up */ + long size; /* size of memory in pool */ + struct pool_t *next; /* known_pools list */ +} pool_t; + +/* known_pools + * Primarily for debugging, keep a list of all active malloc pools. + */ +static pool_t *known_pools = NULL; +static CRITICAL known_pools_lock = NULL; +static unsigned long pool_blocks_created = 0; +static unsigned long pool_blocks_freed = 0; + +/* freelist + * Internally we maintain a list of free blocks which we try to pull from + * whenever possible. This list will never have more than MAX_FREELIST_SIZE + * bytes within it. + */ +static CRITICAL freelist_lock = NULL; +static block_t *freelist = NULL; +static unsigned long freelist_size = 0; +static unsigned long freelist_max = MAX_FREELIST_SIZE; +static int pool_disable = 0; + +int +pool_internal_init() +{ + if (pool_disable == 0) { + if (known_pools_lock == NULL) { + known_pools_lock = crit_init(); + freelist_lock = crit_init(); + } + } else + ereport(LOG_INFORM, XP_GetAdminStr(DBT_poolInitMemoryPoolsDisabled_)); + + return 0; +} + +NSAPI_PUBLIC int +pool_init(pblock *pb, Session *sn, Request *rq) +{ + char *str_free_size = pblock_findval("free-size", pb); + char *str_pool_disable = pblock_findval("disable", pb); + + if (str_free_size != NULL) { + if ( (freelist_max = atoi(str_free_size)) <= 0) { + ereport(LOG_WARN, XP_GetAdminStr(DBT_poolInitFreeSize0UsingD_), + MAX_FREELIST_SIZE); + freelist_max = MAX_FREELIST_SIZE; + } + } + + if (str_pool_disable && strcasecmp(str_pool_disable, "false") ) + pool_disable = 1; + else + pool_disable = 0; + + if (known_pools_lock == NULL) { + known_pools_lock = crit_init(); + freelist_lock = crit_init(); + } + + return REQ_PROCEED; +} + + +static block_t * +_create_block(int size) +{ + block_t *newblock = NULL; + long bytes = ALIGN(size); + block_t *free_ptr, + *last_free_ptr = NULL; + + /* check freelist for large enough block first */ + + crit_enter(freelist_lock); + free_ptr = freelist; + while(free_ptr && ((free_ptr->end - free_ptr->data) < bytes)) { + last_free_ptr = free_ptr; + free_ptr = free_ptr->next; + } + + if (free_ptr) { + newblock = free_ptr; + if (last_free_ptr) + last_free_ptr->next = free_ptr->next; + else + freelist = free_ptr->next; + freelist_size -= (newblock->end - newblock->data); + crit_exit(freelist_lock); + bytes = free_ptr->end - free_ptr->data; + } + else { + pool_blocks_created++; + crit_exit(freelist_lock); + if (((newblock = (block_t *)PERM_MALLOC(sizeof(block_t))) == NULL) || + ((newblock->data = (char *)PERM_MALLOC(bytes)) == NULL)) { + ereport(LOG_CATASTROPHE, XP_GetAdminStr(DBT_poolCreateBlockOutOfMemory_)); + if (newblock) + PERM_FREE(newblock); + return NULL; + } + } + newblock->start = newblock->data; + newblock->end = newblock->data + bytes; + newblock->next = NULL; + + return newblock; +} + +/* Caller must hold lock for the pool */ +static void +_free_block(block_t *block) +{ + +#ifdef POOL_ZERO_DEBUG + memset(block->data, 0xa, block->end-block->data); +#endif /* POOL_ZERO_DEBUG */ + + if ((freelist_size + block->end - block->data) > freelist_max) { + /* Just have to delete the whole block! */ + + crit_enter(freelist_lock); + pool_blocks_freed++; + crit_exit(freelist_lock); + + PERM_FREE(block->data); +#ifdef POOL_ZERO_DEBUG + memset(block, 0xa, sizeof(block)); +#endif /* POOL_ZERO_DEBUG */ + + PERM_FREE(block); + return; + } + crit_enter(freelist_lock); + freelist_size += (block->end - block->data); + block->start = block->data; + + block->next = freelist; + freelist = block; + crit_exit(freelist_lock); +} + +/* ptr_in_pool() + * Checks to see if the given pointer is in the given pool. + * If true, returns a ptr to the block_t containing the ptr; + * otherwise returns NULL + */ +block_t * +_ptr_in_pool(pool_t *pool, void *ptr) +{ + block_t *block_ptr = NULL; + + /* try to find a block which contains this ptr */ + + if ( ((char *)ptr < (char *)pool->curr_block->end) && + ((char *)ptr >= (char *)pool->curr_block->data) ) + block_ptr = pool->curr_block; + else + for( block_ptr = pool->used_blocks; + block_ptr && + (((char *)ptr >= (char *)block_ptr->end) && + ((char *)ptr < (char *)block_ptr->data)); + block_ptr = block_ptr->next); + + return block_ptr; +} + + +NSAPI_PUBLIC pool_handle_t * +pool_create() +{ + pool_t *newpool; + + if (pool_disable) + return NULL; + + newpool = (pool_t *)PERM_MALLOC(sizeof(pool_t)); + + if (newpool) { + /* Have to initialize now, as pools get created sometimes + * before pool_init can be called... + */ + if (known_pools_lock == NULL) { + known_pools_lock = crit_init(); + freelist_lock = crit_init(); + } + + if ( (newpool->curr_block =_create_block(BLOCK_SIZE)) == NULL) { + ereport(LOG_CATASTROPHE, XP_GetAdminStr(DBT_poolCreateOutOfMemory_)); + PERM_FREE(newpool); + return NULL; + } + newpool->used_blocks = NULL; + newpool->size = 0; + newpool->next = NULL; +#ifdef POOL_LOCKING + newpool->lock = crit_init(); +#endif +#ifdef DEBUG_CACHES + newpool->time_created = time(NULL); +#endif + + /* Add to known pools list */ + crit_enter(known_pools_lock); + newpool->next = known_pools; + known_pools = newpool; + crit_exit(known_pools_lock); + } + else + ereport(LOG_CATASTROPHE, XP_GetAdminStr(DBT_poolCreateOutOfMemory_1)); + + return (pool_handle_t *)newpool; +} + +NSAPI_PUBLIC void +pool_destroy(pool_handle_t *pool_handle) +{ + pool_t *pool = (pool_t *)pool_handle; + block_t *tmp_blk; + pool_t *last, *search; + + if (pool_disable) + return; + + crit_enter(known_pools_lock); +#ifdef POOL_LOCKING + crit_enter(pool->lock); +#endif + + if (pool->curr_block) + _free_block(pool->curr_block); + + while(pool->used_blocks) { + tmp_blk = pool->used_blocks; + pool->used_blocks = pool->used_blocks->next; + _free_block(tmp_blk); + } + + /* Remove from the known pools list */ + for (last = NULL, search = known_pools; search; + last = search, search = search->next) + if (search == pool) + break; + if (search) { + if(last) + last->next = search->next; + else + known_pools = search->next; + + } + +#ifdef POOL_LOCKING + crit_exit(pool->lock); + crit_terminate(pool->lock); +#endif + crit_exit(known_pools_lock); + +#ifdef POOL_ZERO_DEBUG + memset(pool, 0xa, sizeof(pool)); +#endif /* POOL_ZERO_DEBUG */ + + PERM_FREE(pool); + + return; +} + + +NSAPI_PUBLIC void * +pool_malloc(pool_handle_t *pool_handle, size_t size) +{ + pool_t *pool = (pool_t *)pool_handle; + long reqsize, blocksize; + char *ptr; + + if (pool == NULL || pool_disable) { + return PERM_MALLOC(size); + } + +#ifdef DEBUG + if (size == 0) + return NULL; +#endif + +#ifdef POOL_LOCKING + crit_enter(pool->lock); +#endif + + reqsize = ALIGN(size); + ptr = pool->curr_block->start; + pool->curr_block->start += reqsize; + + /* does this fit into the last allocated block? */ + if (pool->curr_block->start > pool->curr_block->end) { + + /* Did not fit; time to allocate a new block */ + + pool->curr_block->start -= reqsize; /* keep structs in tact */ + + pool->curr_block->next = pool->used_blocks; + pool->used_blocks = pool->curr_block; + + /* Allocate a chunk of memory which is a multiple of BLOCK_SIZE + * bytes + */ + blocksize = ( (size + BLOCK_SIZE-1) / BLOCK_SIZE ) * BLOCK_SIZE; + if ( (pool->curr_block = _create_block(blocksize)) == NULL) { + ereport(LOG_CATASTROPHE, XP_GetAdminStr(DBT_poolMallocOutOfMemory_)); +#ifdef POOL_LOCKING + crit_exit(pool->lock); +#endif + return NULL; + } + + ptr = pool->curr_block->start; + reqsize = ALIGN(size); + pool->curr_block->start += reqsize; + } + + pool->size += reqsize; + +#ifdef POOL_LOCKING + crit_exit(pool->lock); +#endif + return ptr; +} + +void _pool_free_error() +{ + ereport(LOG_WARN, XP_GetAdminStr(DBT_freeUsedWherePermFreeShouldHaveB_)); + + return; +} + +NSAPI_PUBLIC void +pool_free(pool_handle_t *pool_handle, void *ptr) +{ + if (pool_handle == NULL || pool_disable) { + PERM_FREE(ptr); + return; + } + +#ifdef DEBUG + /* Just to be nice, check to see if the ptr was allocated in a pool. + * If not, issue a warning and do a REAL free just to make sure that + * we don't leak memory. + */ + if ( !_ptr_in_pool((pool_t *)pool_handle, ptr) ) { + _pool_free_error(); + + PERM_FREE(ptr); + } +#endif + return; +} + +NSAPI_PUBLIC void * +pool_calloc(pool_handle_t *pool_handle, size_t nelem, size_t elsize) +{ + void *ptr; + + if (pool_handle == NULL || pool_disable) + return PERM_CALLOC(elsize * nelem); + + ptr = pool_malloc(pool_handle, elsize * nelem); + if (ptr) + memset(ptr, 0, elsize * nelem); + return ptr; +} + +NSAPI_PUBLIC void * +pool_realloc(pool_handle_t *pool_handle, void *ptr, size_t size) +{ + pool_t *pool = (pool_t *)pool_handle; + void *newptr; + block_t *block_ptr; + int oldsize; + + if (pool_handle == NULL || pool_disable) + return PERM_REALLOC(ptr, size); + + if ( (newptr = pool_malloc(pool_handle, size)) == NULL) + return NULL; + + /* With our structure we don't know exactly where the end + * of the original block is. But we do know an upper bound + * which is a valid ptr. Search the outstanding blocks + * for the block which contains this ptr, and copy... + */ +#ifdef POOL_LOCKING + crit_enter(pool->lock); +#endif + + if ( !(block_ptr = _ptr_in_pool(pool, ptr)) ) { + /* User is trying to realloc nonmalloc'd space! */ + return newptr; + } + + oldsize = block_ptr->end - (char *)ptr ; + if (oldsize > size) + oldsize = size; + memmove((char *)newptr, (char *)ptr, oldsize); +#ifdef POOL_LOCKING + crit_exit(pool->lock); +#endif + + return newptr; +} + +NSAPI_PUBLIC char * +pool_strdup(pool_handle_t *pool_handle, const char *orig_str) +{ + char *new_str; + int len = strlen(orig_str); + + if (pool_handle == NULL || pool_disable) + return PERM_STRDUP(orig_str); + + new_str = (char *)pool_malloc(pool_handle, len+1); + + if (new_str) + memcpy(new_str, orig_str, len+1); + + return new_str; +} + +NSAPI_PUBLIC long +pool_space(pool_handle_t *pool_handle) +{ + pool_t *pool = (pool_t *)pool_handle; + + return pool->size; +} + +NSAPI_PUBLIC int pool_enabled() +{ +#ifndef THREAD_ANY + /* we don't have USE_NSPR defined so systhread_getdata is undef'ed */ + return 0; +#else + if (pool_disable || (getThreadMallocKey() == -1) ) + return 0; + + if (!systhread_getdata(getThreadMallocKey())) + return 0; + + return 1; +#endif +} + + +/* pool_service_debug() + * NSAPI service routine to print state information about the existing + * pools. Hopefully useful in debugging. + * + */ +#define MAX_DEBUG_LINE 1024 +#ifdef DEBUG_CACHES /* XXXrobm causes entanglement in install and admserv cgis */ +NSAPI_PUBLIC int +pool_service_debug(pblock *pb, Session *sn, Request *rq) +{ + char tmp_buf[MAX_DEBUG_LINE]; + char cbuf[DEF_CTIMEBUF]; + int len; + pool_t *pool_ptr; + block_t *block_ptr; + int pool_cnt, block_cnt; + + param_free(pblock_remove("content-type", rq->srvhdrs)); + pblock_nvinsert("content-type", "text/html", rq->srvhdrs); + + protocol_status(sn, rq, PROTOCOL_OK, NULL); + protocol_start_response(sn, rq); + + len = util_sprintf(tmp_buf, "<H2>Memory pool status report</H2>\n"); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Note: The 0 block in each pool is \ +the currently used block <P>\n"); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Freelist size: %d/%d<P>", freelist_size, + freelist_max); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Pool disabled: %d<P>", pool_disable); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Blocks created: %d<P> Blocks freed: %d", + pool_blocks_created, pool_blocks_freed); + net_write(sn->csd, tmp_buf, len); + + /* Create an HTML table */ + len = util_sprintf(tmp_buf, "<UL><TABLE BORDER=4>\n"); + net_write(sn->csd, tmp_buf, len); + len = util_sprintf(tmp_buf, "<TH>Pool #</TH>\n"); + net_write(sn->csd, tmp_buf, len); + len = util_sprintf(tmp_buf, "<TH>Pool size #</TH>\n"); + net_write(sn->csd, tmp_buf, len); +#ifdef DEBUG_CACHES + len = util_sprintf(tmp_buf, "<TH>Time Created</TH>\n"); + net_write(sn->csd, tmp_buf, len); +#endif + len = util_sprintf(tmp_buf, "<TH>Blocks</TH>\n"); + net_write(sn->csd, tmp_buf, len); + + crit_enter(known_pools_lock); + for (pool_cnt = 0, pool_ptr = known_pools; pool_ptr; + pool_ptr = pool_ptr->next, pool_cnt++) { + +#ifdef POOL_LOCKING + crit_enter(pool_ptr->lock); +#endif + len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, +#ifndef DEBUG_CACHES +"<tr align=right> <td>%d</td> <td>%d</td> <td> <TABLE BORDER=2> <TH>Block #</TH><TH>data</TH><TH>curr size</TH> <TH>max size</TH>\n", +#else +"<tr align=right> <td>%d</td> <td>%d</td> <td>%s</td> <td> <TABLE BORDER=2> <TH>Block #</TH><TH>data</TH><TH>curr size</TH> <TH>max size</TH>\n", +#endif + pool_cnt, pool_space((pool_handle_t *)pool_ptr) +#ifdef DEBUG_CACHES + , util_ctime(&(pool_ptr->time_created), cbuf, DEF_CTIMEBUF)); +#else + ); +#endif + net_write(sn->csd, tmp_buf, len); + + /* Print the first block */ + len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "\ +<tr align=right> \ +<td>%d</td> \ +<td>%d</td> \ +<td>%d</td> \ +<td>%d</td> \ +</tr>\n", + 0, pool_ptr->curr_block->data, + pool_ptr->curr_block->start -pool_ptr->curr_block->data, + pool_ptr->curr_block->end - pool_ptr->curr_block->data); + + net_write(sn->csd, tmp_buf, len); + + for (block_cnt = 1, block_ptr = pool_ptr->used_blocks; block_ptr; + block_ptr = block_ptr->next, block_cnt++) { + + len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "\ +<tr align=right> \ +<td>%d</td> \ +<td>%d</td> \ +<td>%d</td> \ +<td>%d</td> \ +</tr>\n", + block_cnt, block_ptr->data, + block_ptr->start - block_ptr->data, + block_ptr->end - block_ptr->data); + + net_write(sn->csd, tmp_buf, len); + } +#ifdef POOL_LOCKING + crit_exit(pool_ptr->lock); +#endif + + len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "</TABLE></TD></TR>"); + + net_write(sn->csd, tmp_buf, len); + } + crit_exit(known_pools_lock); + + len = util_sprintf(tmp_buf, "</TABLE></UL>\n"); + net_write(sn->csd, tmp_buf, len); + + return REQ_PROCEED; + +} +#endif /* 0 */ +#endif /* MALLOC_POOLS */ + diff --git a/lib/base/rwlock.cpp b/lib/base/rwlock.cpp new file mode 100644 index 00000000..1b99e062 --- /dev/null +++ b/lib/base/rwlock.cpp @@ -0,0 +1,131 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdlib.h> +#include "crit.h" +#include "rwlock.h" + +/* + * rwLock.c + * Implements a shared/exclusive lock package atop the + * critical section/condition variables. It allows multiple + * shared lock holders and only one exclusive lock holder + * on a lock variable. + * + * NOTE : It currently favors writers over readers and writers + * may starve readers. It is usually preferable to allow updates + * if they are not as frequent. We may have to change this if + * the common usage pattern differs from this. + */ + +typedef struct { + CRITICAL crit; /* Short term crit to synchronize lock ops */ + CONDVAR readFree; /* Indicates lock is free for readers */ + CONDVAR writeFree; /* Indicates lock is free for the writer */ + int numReaders; /* Number of read locks held */ + int write; /* Flag to indicate write lock held */ + int numWriteWaiters;/* Number of threads waiting for write lock */ +} rwLock_t; + +/* + * rwlock_init() + * Allocate and initialize the rwlock structure and return + * to the caller. + */ +RWLOCK rwlock_Init() +{ + rwLock_t *rwLockP; + + rwLockP = (rwLock_t *)PERM_MALLOC(sizeof(rwLock_t)); + rwLockP->numReaders = 0; + rwLockP->write = 0; + rwLockP->numWriteWaiters = 0; + rwLockP->crit = crit_init(); + rwLockP->readFree = condvar_init(rwLockP->crit); + rwLockP->writeFree = condvar_init(rwLockP->crit); + return((RWLOCK)rwLockP); +} + +/* + * rwlock_terminate() + * Terminate the associated condvars and critical sections + */ +void rwlock_Terminate(RWLOCK lockP) +{ + rwLock_t *rwLockP = (rwLock_t *)lockP; + + crit_terminate(rwLockP->crit); + condvar_terminate(rwLockP->readFree); + condvar_terminate(rwLockP->writeFree); + PERM_FREE(rwLockP); +} + +/* + * rwlock_ReadLock -- Obtain a shared lock. The caller would + * block if there are writers or writeWaiters. + */ +void rwlock_ReadLock(RWLOCK lockP) +{ + rwLock_t *rwLockP = (rwLock_t *)lockP; + + crit_enter(rwLockP->crit); + while (rwLockP->write || rwLockP->numWriteWaiters != 0) + condvar_wait(rwLockP->readFree); + rwLockP->numReaders++; + crit_exit(rwLockP->crit); +} + +/* + * rwlock_writeLock -- Obtain an exclusive lock. The caller would + * block if there are other readers or a writer. + */ +void rwlock_WriteLock(RWLOCK lockP) +{ + rwLock_t *rwLockP = (rwLock_t *)lockP; + + crit_enter(rwLockP->crit); + rwLockP->numWriteWaiters++; + while (rwLockP->numReaders != 0 || rwLockP->write) + condvar_wait(rwLockP->writeFree); + rwLockP->numWriteWaiters--; + rwLockP->write = 1; + crit_exit(rwLockP->crit); +} + +/* + * rw_Unlock -- Releases the lock. + */ +void rwlock_Unlock(RWLOCK lockP) +{ + rwLock_t *rwLockP = (rwLock_t *)lockP; + + crit_enter(rwLockP->crit); + if (rwLockP->write) + rwLockP->write = 0; + else + rwLockP->numReaders--; + if (rwLockP->numReaders == 0) + if (rwLockP->numWriteWaiters != 0) + condvar_notify(rwLockP->writeFree); + else + condvar_notifyAll(rwLockP->readFree); + crit_exit(rwLockP->crit); +} + +/* + * rwlock_DemoteLock -- Change an exclusive lock on the given lock + * variable into a shared lock. + */ +void rwlock_DemoteLock(RWLOCK lockP) +{ + rwLock_t *rwLockP = (rwLock_t *)lockP; + + crit_enter(rwLockP->crit); + rwLockP->numReaders = 1; + rwLockP->write = 0; + if (rwLockP->numWriteWaiters == 0) + condvar_notifyAll(rwLockP->readFree); + crit_exit(rwLockP->crit); +} diff --git a/lib/base/shexp.cpp b/lib/base/shexp.cpp new file mode 100644 index 00000000..8cb2aae4 --- /dev/null +++ b/lib/base/shexp.cpp @@ -0,0 +1,290 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shexp.c: shell-like wildcard match routines + * + * + * See shexp.h for public documentation. + * + * Rob McCool + * + */ + +#include "shexp.h" +#include <ctype.h> /* isalpha, tolower */ + + +/* ----------------------------- shexp_valid ------------------------------ */ + + +int valid_subexp(char *exp, char stop) +{ + register int x,y,t; + int nsc,np,tld; + + x=0;nsc=0;tld=0; + + while(exp[x] && (exp[x] != stop)) { + switch(exp[x]) { + case '~': + if(tld) return INVALID_SXP; + else ++tld; + case '*': + case '?': + case '^': + case '$': + ++nsc; + break; + case '[': + ++nsc; + if((!exp[++x]) || (exp[x] == ']')) + return INVALID_SXP; + for(++x;exp[x] && (exp[x] != ']');++x) + if(exp[x] == '\\') + if(!exp[++x]) + return INVALID_SXP; + if(!exp[x]) + return INVALID_SXP; + break; + case '(': + ++nsc; + while(1) { + if(exp[++x] == ')') + return INVALID_SXP; + for(y=x;(exp[y]) && (exp[y] != '|') && (exp[y] != ')');++y) + if(exp[y] == '\\') + if(!exp[++y]) + return INVALID_SXP; + if(!exp[y]) + return INVALID_SXP; + t = valid_subexp(&exp[x],exp[y]); + if(t == INVALID_SXP) + return INVALID_SXP; + x+=t; + if(exp[x] == ')') { + break; + } + } + break; + case ')': + case ']': + return INVALID_SXP; + case '\\': + if(!exp[++x]) + return INVALID_SXP; + default: + break; + } + ++x; + } + if((!stop) && (!nsc)) + return NON_SXP; + return ((exp[x] == stop) ? x : INVALID_SXP); +} + +NSAPI_PUBLIC int shexp_valid(char *exp) { + int x; + + x = valid_subexp(exp, '\0'); + return (x < 0 ? x : VALID_SXP); +} + + +/* ----------------------------- shexp_match ----------------------------- */ + + +#define MATCH 0 +#define NOMATCH 1 +#define ABORTED -1 + +int _shexp_match(char *str, char *exp); + +int handle_union(char *str, char *exp) +{ + char *e2 = (char *) MALLOC(sizeof(char)*strlen(exp)); + register int t,p2,p1 = 1; + int cp; + + while(1) { + for(cp=1;exp[cp] != ')';cp++) + if(exp[cp] == '\\') + ++cp; + for(p2 = 0;(exp[p1] != '|') && (p1 != cp);p1++,p2++) { + if(exp[p1] == '\\') + e2[p2++] = exp[p1++]; + e2[p2] = exp[p1]; + } + for(t=cp+1;(e2[p2] = exp[t]);++t,++p2); + if(_shexp_match(str,e2) == MATCH) { + FREE(e2); + return MATCH; + } + if(p1 == cp) { + FREE(e2); + return NOMATCH; + } + else ++p1; + } +} + + +int _shexp_match(char *str, char *exp) +{ + register int x,y; + int ret,neg; + + ret = 0; + for(x=0,y=0;exp[y];++y,++x) { + if((!str[x]) && (exp[y] != '(') && (exp[y] != '$') && (exp[y] != '*')) + ret = ABORTED; + else { + switch(exp[y]) { + case '$': + if( (str[x]) ) + ret = NOMATCH; + else + --x; /* we don't want loop to increment x */ + break; + case '*': + while(exp[++y] == '*'); + if(!exp[y]) + return MATCH; + while(str[x]) { + switch(_shexp_match(&str[x++],&exp[y])) { + case NOMATCH: + continue; + case ABORTED: + ret = ABORTED; + break; + default: + return MATCH; + } + break; + } + if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x])) + return MATCH; + else + ret = ABORTED; + break; + case '[': + if((neg = ((exp[++y] == '^') && (exp[y+1] != ']')))) + ++y; + + if((isalnum(exp[y])) && (exp[y+1] == '-') && + (isalnum(exp[y+2])) && (exp[y+3] == ']')) + { + int start = exp[y], end = exp[y+2]; + + /* Droolproofing for pinheads not included */ + if(neg ^ ((str[x] < start) || (str[x] > end))) { + ret = NOMATCH; + break; + } + y+=3; + } + else { + int matched; + + for(matched=0;exp[y] != ']';y++) + matched |= (str[x] == exp[y]); + if(neg ^ (!matched)) + ret = NOMATCH; + } + break; + case '(': + return handle_union(&str[x],&exp[y]); + break; + case '?': + break; + case '\\': + ++y; + default: +#ifdef XP_UNIX + if(str[x] != exp[y]) +#else /* XP_WIN32 */ + if(strnicmp(str + x, exp + y, 1)) +#endif /* XP_WIN32 */ + ret = NOMATCH; + break; + } + } + if(ret) + break; + } + return (ret ? ret : (str[x] ? NOMATCH : MATCH)); +} + +NSAPI_PUBLIC int shexp_match(char *str, char *xp) { + register int x; + char *exp = STRDUP(xp); + + for(x=strlen(exp)-1;x;--x) { + if((exp[x] == '~') && (exp[x-1] != '\\')) { + exp[x] = '\0'; + if(_shexp_match(str,&exp[++x]) == MATCH) + goto punt; + break; + } + } + if(_shexp_match(str,exp) == MATCH) { + FREE(exp); + return 0; + } + + punt: + FREE(exp); + return 1; +} + + +/* ------------------------------ shexp_cmp ------------------------------- */ + + +NSAPI_PUBLIC int shexp_cmp(char *str, char *exp) +{ + switch(shexp_valid(exp)) { + case INVALID_SXP: + return -1; + case NON_SXP: +#ifdef XP_UNIX + return (strcmp(exp,str) ? 1 : 0); +#else /* XP_WIN32 */ + return (stricmp(exp,str) ? 1 : 0); +#endif /* XP_WIN32 */ + default: + return shexp_match(str, exp); + } +} + + +/* ---------------------------- shexp_casecmp ----------------------------- */ + + +NSAPI_PUBLIC int shexp_casecmp(char *str, char *exp) +{ + char *lstr = STRDUP(str), *lexp = STRDUP(exp), *t; + int ret; + + for(t = lstr; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + for(t = lexp; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + + switch(shexp_valid(lexp)) { + case INVALID_SXP: + ret = -1; + break; + case NON_SXP: + ret = (strcmp(lexp, lstr) ? 1 : 0); + break; + default: + ret = shexp_match(lstr, lexp); + } + FREE(lstr); + FREE(lexp); + return ret; +} + diff --git a/lib/base/shmem.cpp b/lib/base/shmem.cpp new file mode 100644 index 00000000..1ddd76d3 --- /dev/null +++ b/lib/base/shmem.cpp @@ -0,0 +1,127 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shmem.h: Portable abstraction for memory shared among a server's workers + * + * Rob McCool + */ + + +#include "shmem.h" + +#if defined (SHMEM_UNIX_MMAP) + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <private/pprio.h> /* for nspr20 binary release */ + +NSPR_BEGIN_EXTERN_C +#include <sys/mman.h> +NSPR_END_EXTERN_C + +NSAPI_PUBLIC shmem_s *shmem_alloc(char *name, int size, int expose) +{ + shmem_s *ret = (shmem_s *) PERM_MALLOC(sizeof(shmem_s)); + char *growme; + + if( (ret->fd = PR_Open(name, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0666)) == NULL) { + PERM_FREE(ret); + return NULL; + } + growme = (char *) PERM_MALLOC(size); + ZERO(growme, size); + if(PR_Write(ret->fd, (char *)growme, size) < 0) { + PR_Close(ret->fd); + PERM_FREE(growme); + PERM_FREE(ret); + return NULL; + } + PERM_FREE(growme); + PR_Seek(ret->fd, 0, PR_SEEK_SET); + if( (ret->data = (char *)mmap(NULL, size, PROT_READ | PROT_WRITE, + SHMEM_MMAP_FLAGS, PR_FileDesc2NativeHandle(ret->fd), 0)) == (caddr_t) -1) + { + PR_Close(ret->fd); + PERM_FREE(ret); + return NULL; + } + if(!expose) { + ret->name = NULL; + unlink(name); + } + else + ret->name = STRDUP(name); + ret->size = size; + return ret; +} + + +NSAPI_PUBLIC void shmem_free(shmem_s *region) +{ + if(region->name) { + unlink(region->name); + PERM_FREE(region->name); + } + munmap((char *)region->data, region->size); /* CLEARLY, C++ SUCKS */ + PR_Close(region->fd); + PERM_FREE(region); +} + +#elif defined (SHMEM_WIN32_MMAP) + +#define PAGE_SIZE (1024*8) +#define ALIGN(x) ( (x+PAGE_SIZE-1) & (~(PAGE_SIZE-1)) ) +NSAPI_PUBLIC shmem_s *shmem_alloc(char *name, int size, int expose) +{ + shmem_s *ret = (shmem_s *) PERM_MALLOC(sizeof(shmem_s)); + HANDLE fHandle; + + ret->fd = 0; /* not used on NT */ + + size = ALIGN(size); + if( !(ret->fdmap = CreateFileMapping( + (HANDLE)0xffffffff, + NULL, + PAGE_READWRITE, + 0, + size, + name)) ) + { + int err = GetLastError(); + PERM_FREE(ret); + return NULL; + } + if( !(ret->data = (char *)MapViewOfFile ( + ret->fdmap, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0)) ) + { + CloseHandle(ret->fdmap); + PERM_FREE(ret); + return NULL; + } + ret->size = size; + ret->name = NULL; + + return ret; +} + + +NSAPI_PUBLIC void shmem_free(shmem_s *region) +{ + if(region->name) { + DeleteFile(region->name); + PERM_FREE(region->name); + } + UnmapViewOfFile(region->data); + CloseHandle(region->fdmap); + PERM_FREE(region); +} + +#endif /* SHMEM_WIN32_MMAP */ diff --git a/lib/base/system.cpp b/lib/base/system.cpp new file mode 100644 index 00000000..e58538b4 --- /dev/null +++ b/lib/base/system.cpp @@ -0,0 +1,264 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * system.c: A grab bag of system-level abstractions + * + * Many authors + */ + +#include "netsite.h" +#include "base/nsassert.h" +#include "base/ereport.h" + +#ifdef XP_WIN32 +#include <windows.h> +static char *version = "Netscape Server/2.0"; +#endif + +#include "base/systems.h" /* find out if we have malloc pools */ + +static int thread_malloc_key = -1; + +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) +#include "base/pool.h" +#include "base/systhr.h" + +#define MALLOC_KEY \ + ((pool_handle_t *)(thread_malloc_key != -1 ? systhread_getdata(thread_malloc_key) : NULL)) + +#endif + + +#ifdef MCC_DEBUG +#define DEBUG_MALLOC +#endif + +#ifdef DEBUG_MALLOC + +/* The debug malloc routines provide several functions: + * + * - detect allocated memory overflow/underflow + * - detect multiple frees + * - intentionally clobbers malloc'd buffers + * - intentionally clobbers freed buffers + */ +#define DEBUG_MAGIC 0x12345678 +#define DEBUG_MARGIN 32 +#define DEBUG_MARGIN_CHAR '*' +#define DEBUG_MALLOC_CHAR '.' +#define DEBUG_FREE_CHAR 'X' +#endif /* DEBUG_MALLOC */ + +/* On NT, the server version string is not statically encoded based + * upon a product compile define but dynamically set by the server + * exe at startup. + */ + +NSAPI_PUBLIC char *system_version() +{ +#ifdef XP_WIN32 + return version; +#else /* XP_UNIX */ + return MAGNUS_VERSION_STRING; +#endif /* XP_UNIX */ +} + +NSAPI_PUBLIC void system_version_set(char *server_version) +{ +#ifdef XP_WIN32 + version = PERM_STRDUP(server_version); +#endif +} + + +NSAPI_PUBLIC void *system_malloc(int size) +{ +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) + return pool_malloc(MALLOC_KEY, size); +#else + return malloc(size); +#endif +} + + +NSAPI_PUBLIC void *system_calloc(int size) +{ + void *ret; +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) + ret = pool_malloc(MALLOC_KEY, size); +#else + ret = malloc(size); +#endif + if(ret) + ZERO(ret, size); + return ret; +} + + +NSAPI_PUBLIC void *system_realloc(void *ptr, int size) +{ +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) + return pool_realloc(MALLOC_KEY, ptr, size); +#else + return realloc(ptr, size); +#endif +} + + +NSAPI_PUBLIC void system_free(void *ptr) +{ +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) + pool_free(MALLOC_KEY, ptr); +#else + NS_ASSERT(ptr); + free(ptr); +#endif +} + +NSAPI_PUBLIC char *system_strdup(const char *ptr) +{ + NS_ASSERT(ptr); +#if defined(MALLOC_POOLS) && defined(MCC_HTTPD) && defined(THREAD_ANY) + return pool_strdup(MALLOC_KEY, ptr); +#else + return strdup(ptr); +#endif +} + + +NSAPI_PUBLIC void *system_malloc_perm(int size) +{ +#ifndef DEBUG_MALLOC + return malloc(size); +#else + char *ptr = (char *)malloc(size + 2*DEBUG_MARGIN+2*sizeof(int)); + char *real_ptr; + int *magic; + int *length; + + magic = (int *)ptr; + *magic = DEBUG_MAGIC; + ptr += sizeof(int); + length = (int *)ptr; + *length = size; + ptr += sizeof(int); + memset(ptr, DEBUG_MARGIN_CHAR, DEBUG_MARGIN); + ptr += DEBUG_MARGIN; + memset(ptr, DEBUG_MALLOC_CHAR, size); + real_ptr = ptr; + ptr += size; + memset(ptr, DEBUG_MARGIN_CHAR, DEBUG_MARGIN); + + return real_ptr; +#endif +} + +NSAPI_PUBLIC void *system_calloc_perm(int size) +{ + void *ret = system_malloc_perm(size); + if(ret) + ZERO(ret, size); + return ret; +} + +NSAPI_PUBLIC void *system_realloc_perm(void *ptr, int size) +{ +#ifndef DEBUG_MALLOC + return realloc(ptr, size); +#else + int *magic, *length; + char *baseptr; + char *cptr; + + cptr = (char *)ptr - DEBUG_MARGIN - 2 * sizeof(int); + magic = (int *)cptr; + if (*magic == DEBUG_MAGIC) { + cptr += sizeof(int); + length = (int *)cptr; + if (*length < size) { + char *newptr = (char *)system_malloc_perm(size); + memcpy(newptr, ptr, *length); + system_free_perm(ptr); + + return newptr; + }else { + return ptr; + } + } else { + ereport(LOG_WARN, "realloc: attempt to realloc to smaller size"); + return realloc(ptr, size); + } + +#endif +} + +NSAPI_PUBLIC void system_free_perm(void *ptr) +{ +#ifdef DEBUG_MALLOC + int *length, *magic; + char *baseptr, *cptr; + int index; + + NS_ASSERT(ptr); + + cptr = baseptr = ((char *)ptr) - DEBUG_MARGIN - 2*sizeof(int); + + magic = (int *)cptr; + if (*magic == DEBUG_MAGIC) { + cptr += sizeof(int); + + length = (int *)cptr; + + cptr += sizeof(int); + for (index=0; index<DEBUG_MARGIN; index++) + if (cptr[index] != DEBUG_MARGIN_CHAR) { + ereport(LOG_CATASTROPHE, "free: corrupt memory (prebounds overwrite)"); + break; + } + + cptr += DEBUG_MARGIN + *length; + for (index=0; index<DEBUG_MARGIN; index++) + if (cptr[index] != DEBUG_MARGIN_CHAR) { + ereport(LOG_CATASTROPHE, "free: corrupt memory (prebounds overwrite)"); + break; + } + + memset(baseptr, DEBUG_FREE_CHAR, *length + 2*DEBUG_MARGIN+sizeof(int)); + } else { + ereport(LOG_CATASTROPHE, "free: freeing unallocated memory"); + } + free(baseptr); +#else + free(ptr); +#endif +} + +NSAPI_PUBLIC char *system_strdup_perm(const char *ptr) +{ +#ifndef DEBUG_MALLOC + NS_ASSERT(ptr); + return strdup(ptr); +#else + int len = strlen(ptr); + char *nptr = (char *)system_malloc_perm(len+1); + memcpy(nptr, ptr, len); + nptr[len] = '\0'; + return nptr; +#endif +} + +NSAPI_PUBLIC int +getThreadMallocKey(void) +{ + return thread_malloc_key; +} + +void +setThreadMallocKey(int key) +{ + thread_malloc_key = key; +} + diff --git a/lib/base/systhr.cpp b/lib/base/systhr.cpp new file mode 100644 index 00000000..ac29121a --- /dev/null +++ b/lib/base/systhr.cpp @@ -0,0 +1,256 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * systhr.c: Abstracted threading mechanisms + * + * Rob McCool + */ + +#include "systhr.h" +#include "ereport.h" + +#define USE_NSPR +#ifdef USE_NSPR +#include "nspr.h" +#include "private/prpriv.h" +extern "C" { +int32 PR_GetSysfdTableMax(void); +int32 PR_SetSysfdTableSize(int table_size); +} +#endif +#include "systems.h" + +#ifdef THREAD_WIN32 +#include <process.h> + +typedef struct { + HANDLE hand; + DWORD id; +} sys_thread_s; + +#endif + + + +#if defined (USE_NSPR) + + +#define DEFAULT_STACKSIZE (64*1024) + +static unsigned long _systhr_stacksize = DEFAULT_STACKSIZE; + +NSAPI_PUBLIC +void systhread_set_default_stacksize(unsigned long size) +{ + _systhr_stacksize = size; +} + +NSPR_BEGIN_EXTERN_C + +NSAPI_PUBLIC SYS_THREAD +#ifdef UnixWare /* for ANSI C++ standard, see base/systrh.h */ +systhread_start(int prio, int stksz, ArgFn_systhread_start fn, void *arg) +#else +systhread_start(int prio, int stksz, void (*fn)(void *), void *arg) +#endif +{ +#if (defined(Linux) || defined(SNI) || defined(UnixWare)) && !defined(USE_PTHREADS) + prio /= 8; /* quick and dirty fix for user thread priority scale problem */ + if (prio > 3) prio = 3; +#endif + + PRThread *ret = PR_CreateThread(PR_USER_THREAD, (void (*)(void *))fn, + (void *)arg, (PRThreadPriority)prio, + PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + stksz ? stksz : _systhr_stacksize); + return (void *) ret; +} + +NSPR_END_EXTERN_C + + +NSAPI_PUBLIC SYS_THREAD systhread_current(void) +{ + return PR_GetCurrentThread(); +} + +NSAPI_PUBLIC void systhread_yield(void) +{ + /* PR_Yield(); */ + PR_Sleep(PR_INTERVAL_NO_WAIT); +} + + +NSAPI_PUBLIC void systhread_timerset(int usec) +{ + /* This is an interesting problem. If you ever do turn on interrupts + * on the server, you're in for lots of fun with NSPR Threads + PR_StartEvents(usec); */ +} + + +NSAPI_PUBLIC +SYS_THREAD systhread_attach(void) +{ + PRThread *ret; + ret = PR_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL); + + return (void *) ret; +} + +NSAPI_PUBLIC +void systhread_detach(SYS_THREAD thr) +{ + /* XXXMB - this is not correct! */ + PR_DetachThread(); +} + +NSAPI_PUBLIC void systhread_terminate(SYS_THREAD thr) +{ + + /* Should never be here. PR_DestroyThread is no + * longer used. */ + PR_ASSERT(0); + + /* PR_DestroyThread((PRThread *) thr); */ +} + +NSAPI_PUBLIC void systhread_sleep(int milliseconds) +{ + PR_Sleep(milliseconds); +} + +NSAPI_PUBLIC void systhread_init(char *name) +{ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); +#ifdef XP_UNIX + /* XXXrobm allocate all the fd's we can... */ + PR_SetSysfdTableSize(PR_GetSysfdTableMax()); +#endif +} + + +NSAPI_PUBLIC int systhread_newkey() +{ + uintn newkey; + + PR_NewThreadPrivateIndex(&newkey, NULL); + return (newkey); +} + +NSAPI_PUBLIC void *systhread_getdata(int key) +{ + return PR_GetThreadPrivate(key); +} + +NSAPI_PUBLIC void systhread_setdata(int key, void *data) +{ + PR_SetThreadPrivate(key, data); +} + +/* + * Drag in the Java code, so our dynamic library full of it works + * i.e. force these symbols to load. + */ +NSAPI_PUBLIC void systhread_dummy(void) +{ + +#ifndef NSPR20 + /* nspr/gc.c */ + PR_InitGC(0,0); + /* nspr/prsystem.c */ + PR_GetSystemInfo(PR_SI_SYSNAME, 0, 0); + /* nspr/linker.c */ + PR_GetLibName(0, 0); + /* nspr/file.c */ + PR_Mkdir(0, 0); + /* nspr/prnetdb.c */ + PR_gethostbyname(0, 0, 0, 0, 0); + /* nspr/longlong.c */ + LL_TO_S(LL_ZERO, 0, NULL, 0); +#endif /* NSPR20 */ +} + +#elif defined(THREAD_WIN32) + +#include <nspr/prthread.h> +#define DEFAULT_STACKSIZE 262144 + +NSPR_BEGIN_EXTERN_C + +NSAPI_PUBLIC +SYS_THREAD systhread_start(int prio, int stksz, void (*fn)(void *), void *arg) +{ + sys_thread_s *ret = (sys_thread_s *) MALLOC(sizeof(sys_thread_s)); + + if ((ret->hand = (HANDLE)_beginthreadex(NULL, stksz, (unsigned (__stdcall *)(void *))fn, + arg, 0, &ret->id)) == 0) { + FREE(ret); + return NULL; + } + return (void *)ret; +} + +NSPR_END_EXTERN_C + +NSAPI_PUBLIC SYS_THREAD systhread_current(void) +{ + /* XXXrobm this is busted.... */ + return GetCurrentThread(); +} + +NSAPI_PUBLIC void systhread_timerset(int usec) +{ +} + +NSAPI_PUBLIC SYS_THREAD systhread_attach(void) +{ + return NULL; +} + +NSAPI_PUBLIC void systhread_yield(void) +{ + systhread_sleep(0); +} + +NSAPI_PUBLIC void systhread_terminate(SYS_THREAD thr) +{ + TerminateThread(((sys_thread_s *)thr)->hand, 0); +} + + +NSAPI_PUBLIC void systhread_sleep(int milliseconds) +{ + /* XXXrobm there must be a better way to do this */ + HANDLE sem = CreateSemaphore(NULL, 1, 4, "sleeper"); + WaitForSingleObject(sem, INFINITE); + WaitForSingleObject(sem, milliseconds); + CloseHandle(sem); +} + +NSAPI_PUBLIC void systhread_init(char *name) +{ + PR_Init(PR_USER_THREAD, 1, 0); +} + + +NSAPI_PUBLIC int systhread_newkey() +{ + return TlsAlloc(); +} + +NSAPI_PUBLIC void *systhread_getdata(int key) +{ + return (void *)TlsGetValue(key); +} + +NSAPI_PUBLIC void systhread_setdata(int key, void *data) +{ + TlsSetValue(key, data); +} + +#endif + diff --git a/lib/base/util.cpp b/lib/base/util.cpp new file mode 100644 index 00000000..55d6ca56 --- /dev/null +++ b/lib/base/util.cpp @@ -0,0 +1,1449 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * util.c: A hodge podge of utility functions and standard functions which + * are unavailable on certain systems + * + * Rob McCool + */ + +#ifdef XP_UNIX +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include "prthread.h" +#endif /* XP_UNIX */ + +#include "base/util.h" + +#include "base/dbtbase.h" +#include "base/ereport.h" + + +#ifdef XP_UNIX +#include <sys/types.h> +#endif /* WIN32 */ + +/* ----------------------------- util_getline ----------------------------- */ + +#define LF 10 +#define CR 13 + +NSAPI_PUBLIC int util_getline(filebuf_t *buf, int lineno, int maxlen, char *l) { + int i, x; + + x = 0; + while(1) { + i = filebuf_getc(buf); + switch(i) { + case IO_EOF: + l[x] = '\0'; + return 1; + case LF: + if(x && (l[x-1] == '\\')) { + --x; + continue; + } + l[x] = '\0'; + return 0; + case IO_ERROR: + util_sprintf(l, "I/O error reading file at line %d", lineno); + return -1; + case CR: + continue; + default: + l[x] = (char) i; + if(++x == maxlen) { + util_sprintf(l, "line %d is too long", lineno); + return -1; + } + break; + } + } +} + + +/* ---------------------------- util_can_exec ----------------------------- */ + +#ifdef XP_UNIX +NSAPI_PUBLIC int util_can_exec(struct stat *fi, uid_t uid, gid_t gid) +{ + if(!uid) + return 1; + if((fi->st_mode & S_IXOTH) || + ((gid == fi->st_gid) && (fi->st_mode & S_IXGRP)) || + ((uid == fi->st_uid) && (fi->st_mode & S_IXUSR))) + return 1; + return 0; +} +#endif /* XP_UNIX */ + + +/* --------------------------- util_env_create ---------------------------- */ + + +NSAPI_PUBLIC char **util_env_create(char **env, int n, int *pos) +{ + int x; + + if(!env) { + *pos = 0; + return (char **) MALLOC((n + 1)*sizeof(char *)); + } + else { + for(x = 0; (env[x]); x++); + env = (char **) REALLOC(env, (n + x + 1)*(sizeof(char *))); + *pos = x; + return env; + } +} + + +/* ---------------------------- util_env_free ----------------------------- */ + + +NSAPI_PUBLIC void util_env_free(char **env) +{ + register char **ep = env; + + for(ep = env; *ep; ep++) + FREE(*ep); + FREE(env); +} + +/* ----------------------------- util_env_str ----------------------------- */ + + +NSAPI_PUBLIC char *util_env_str(char *name, char *value) { + char *t,*tp; + + t = (char *) MALLOC(strlen(name)+strlen(value)+2); /* 2: '=' and '\0' */ + + for(tp=t; (*tp = *name); tp++,name++); + for(*tp++ = '='; (*tp = *value); tp++,value++); + return t; +} + + +/* --------------------------- util_env_replace --------------------------- */ + + +NSAPI_PUBLIC void util_env_replace(char **env, char *name, char *value) +{ + int x, y, z; + char *i; + + for(x = 0; env[x]; x++) { + i = strchr(env[x], '='); + *i = '\0'; + if(!strcmp(env[x], name)) { + y = strlen(env[x]); + z = strlen(value); + + env[x] = (char *) REALLOC(env[x], y + z + 2); + util_sprintf(&env[x][y], "=%s", value); + return; + } + *i = '='; + } +} + + +/* ---------------------------- util_env_find ----------------------------- */ + + +NSAPI_PUBLIC char *util_env_find(char **env, char *name) +{ + char *i; + int x, r; + + for(x = 0; env[x]; x++) { + i = strchr(env[x], '='); + *i = '\0'; + r = !strcmp(env[x], name); + *i = '='; + if(r) + return i + 1; + } + return NULL; +} + + +/* ---------------------------- util_env_copy ----------------------------- */ + + +NSAPI_PUBLIC char **util_env_copy(char **src, char **dst) +{ + char **src_ptr; + int src_cnt; + int index; + + if (!src) + return NULL; + + for (src_cnt = 0, src_ptr = src; *src_ptr; src_ptr++, src_cnt++); + + if (!src_cnt) + return NULL; + + dst = util_env_create(dst, src_cnt, &index); + + for (src_ptr = src, index=0; *src_ptr; index++, src_ptr++) + dst[index] = STRDUP(*src_ptr); + dst[index] = NULL; + + return dst; +} + +/* ---------------------------- util_hostname ----------------------------- */ + + +/* + * MOVED TO NET.C TO AVOID INTERDEPENDENCIES + */ + + +/* --------------------------- util_chdir2path ---------------------------- */ + + +NSAPI_PUBLIC int util_chdir2path(char *path) +{ + /* use FILE_PATHSEP to accomodate WIN32 */ + char *t = strrchr(path, FILE_PATHSEP); + int ret; + + if(!t) + return -1; + + *t = '\0'; +#ifdef XP_UNIX + ret = chdir(path); +#else /* WIN32 */ + ret = SetCurrentDirectory(path); +#endif /* XP_UNIX */ + + /* use FILE_PATHSEP instead of chdir to accomodate WIN32 */ + *t = FILE_PATHSEP; + + return ret; +} + + +/* --------------------------- util_is_mozilla ---------------------------- */ + + +NSAPI_PUBLIC int util_is_mozilla(char *ua, char *major, char *minor) +{ + if((!ua) || strncasecmp(ua, "Mozilla/", 8)) + return 0; + + /* Major version. I punted on supporting versions like 10.0 */ + if(ua[8] > major[0]) + return 1; + else if((ua[8] < major[0]) || (ua[9] != '.')) + return 0; + + /* Minor version. Support version numbers like 0.96 */ + if(ua[10] < minor[0]) + return 0; + else if((ua[10] > minor[0]) || (!minor[1])) + return 1; + + if((!isdigit(ua[11])) || (ua[11] < minor[1])) + return 0; + else + return 1; +} + + +/* ----------------------------- util_is_url ------------------------------ */ + + +#include <ctype.h> /* isalpha */ + +NSAPI_PUBLIC int util_is_url(char *url) +{ + char *t = url; + + while(*t) { + if(*t == ':') + return 1; + if(!isalpha(*t)) + return 0; + ++t; + } + return 0; +} + + +/* --------------------------- util_later_than ---------------------------- */ + + +int _mstr2num(char *str) { + if(!strcasecmp(str, "Jan")) return 0; + if(!strcasecmp(str, "Feb")) return 1; + if(!strcasecmp(str, "Mar")) return 2; + if(!strcasecmp(str, "Apr")) return 3; + if(!strcasecmp(str, "May")) return 4; + if(!strcasecmp(str, "Jun")) return 5; + if(!strcasecmp(str, "Jul")) return 6; + if(!strcasecmp(str, "Aug")) return 7; + if(!strcasecmp(str, "Sep")) return 8; + if(!strcasecmp(str, "Oct")) return 9; + if(!strcasecmp(str, "Nov")) return 10; + if(!strcasecmp(str, "Dec")) return 11; + return -1; +} + +int _time_compare(struct tm *lms, char *ims, int later_than_op) +{ + int y = 0, mnum = 0, d = 0, h = 0, m = 0, s = 0, x; + char t[128]; + + /* Supported formats start with weekday (which we don't care about) */ + /* The sizeof(t) is to avoid buffer overflow with t */ + if((!(ims = strchr(ims,' '))) || (strlen(ims) > (sizeof(t) - 2))) + return 0; + + while(*ims && isspace(*ims)) ++ims; + if((!(*ims)) || (strlen(ims) < 2)) + return 0; + + /* Standard HTTP (RFC 850) starts with dd-mon-yy */ + if(ims[2] == '-') { + sscanf(ims, "%s %d:%d:%d", t, &h, &m, &s); + if(strlen(t) < 6) + return 0; + t[2] = '\0'; + t[6] = '\0'; + d = atoi(t); + mnum = _mstr2num(&t[3]); + x = atoi(&t[7]); + /* Postpone wraparound until 2070 */ + y = x + (x < 70 ? 2000 : 1900); + } + /* The ctime format starts with a month name */ + else if(isalpha(*ims)) { + sscanf(ims,"%s %d %d:%d:%d %*s %d", t, &d, &h, &m, &s, &y); + mnum = _mstr2num(t); + } + /* RFC 822 */ + else { + sscanf(ims, "%d %s %d %d:%d:%d", &d, t, &y, &h, &m, &s); + mnum = _mstr2num(t); + } + + if (later_than_op) { + if( (x = (1900 + lms->tm_year) - y) ) + return x < 0; + + if(mnum == -1) + return 0; + + /* XXXMB - this will fail if you check if december 31 1996 is later + * than january 1 1997 + */ + if((x = lms->tm_mon - mnum) || (x = lms->tm_mday - d) || + (x = lms->tm_hour - h) || (x = lms->tm_min - m) || + (x = lms->tm_sec - s)) + return x < 0; + + return 1; + } + else { + return (mnum != -1 && + 1900 + lms->tm_year == y && + lms->tm_mon == mnum && + lms->tm_mday == d && + lms->tm_hour == h && + lms->tm_min == m && + lms->tm_sec == s); + } +} + + +/* Returns 0 if lms later than ims + * Returns 1 if equal + * Returns 1 if ims later than lms + */ +NSAPI_PUBLIC int util_later_than(struct tm *lms, char *ims) +{ + return _time_compare(lms, ims, 1); +} + + +NSAPI_PUBLIC int util_time_equal(struct tm *lms, char *ims) +{ + return _time_compare(lms, ims, 0); +} + +/* util_str_time_equal() + * + * Function to compare if two time strings are equal + * + * Acceptible date formats: + * Saturday, 17-Feb-96 19:41:34 GMT <RFC850> + * Sat, 17 Mar 1996 19:41:34 GMT <RFC1123> + * + * Argument t1 MUST be RFC1123 format. + * + * Note- it is not the intention of this routine to *always* match + * There are cases where we would return != when the strings might + * be equal (especially with case). The converse should not be true. + * + * Return 0 if equal, -1 if not equal. + */ +#define MINIMUM_LENGTH 18 +#define RFC1123_DAY 5 +#define RFC1123_MONTH 8 +#define RFC1123_YEAR 12 +#define RFC1123_HOUR 17 +#define RFC1123_MINUTE 20 +#define RFC1123_SECOND 23 +NSAPI_PUBLIC int util_str_time_equal(char *t1, char *t2) +{ + int index; + + /* skip over leading whitespace... */ + while(*t1 && isspace(*t1)) ++t1; + while(*t2 && isspace(*t2)) ++t2; + + /* Check weekday */ + if ( (t1[0] != t2[0]) || (t1[1] != t2[1]) ) + return -1; + + /* Skip to date */ + while(*t2 && !isspace(*t2)) ++t2; + t2++; + + /* skip if not strings not long enough */ + if ( (strlen(t1) < MINIMUM_LENGTH) || (strlen(t2) < MINIMUM_LENGTH) ) + return -1; + + if ( (t1[RFC1123_DAY] != t2[0]) || (t1[RFC1123_DAY+1] != t2[1]) ) + return -1; + + /* Skip to the month */ + t2 += 3; + + if ( (t1[RFC1123_MONTH] != t2[0]) || (t1[RFC1123_MONTH+1] != t2[1]) || + (t1[RFC1123_MONTH+2] != t2[2]) ) + return -1; + + /* Skip to year */ + t2 += 4; + + if ( (t1[RFC1123_YEAR] != t2[0]) ) { + /* Assume t2 is RFC 850 format */ + if ( (t1[RFC1123_YEAR+2] != t2[0]) || (t1[RFC1123_YEAR+3] != t2[1]) ) + return -1; + + /* skip to hour */ + t2 += 3; + } else { + /* Assume t2 is RFC 1123 format */ + if ( (t1[RFC1123_YEAR+1] != t2[1]) || (t1[RFC1123_YEAR+2] != t2[2]) || + (t1[RFC1123_YEAR+3] != t2[3]) ) + return -1; + + /* skip to hour */ + t2 += 5; + } + + /* check date */ + for (index=0; index<8; index++) { + if ( t1[RFC1123_HOUR+index] != t2[index] ) + return -1; + } + + /* Ignore timezone */ + + return 0; +} + + +/* --------------------------- util_uri_is_evil --------------------------- */ + + +NSAPI_PUBLIC int util_uri_is_evil(char *t) +{ + register int x; + + for(x = 0; t[x]; ++x) { + if(t[x] == '/') { + if(t[x+1] == '/') + return 1; + if(t[x+1] == '.') { + switch(t[x+2]) { + case '.': + if((!t[x+3]) || (t[x+3] == '/')) + return 1; + case '/': + case '\0': + return 1; + } + } + } +#ifdef XP_WIN32 + /* On NT, the directory "abc...." is the same as "abc" + * The only cheap way to catch this globally is to disallow + * names with the trailing "."s. Hopefully this is not over + * restrictive + */ + if ((t[x] == '.') && ( (t[x+1] == '/') || (t[x+1] == '\0') )) { + return 1; + } +#endif + } + return 0; +} + +/* ---------------------------- util_uri_parse ---------------------------- */ + +NSAPI_PUBLIC void util_uri_parse(char *uri) +{ + int spos = 0, tpos = 0; + int l = strlen(uri); + + while(uri[spos]) { + if(uri[spos] == '/') { + if((spos != l) && (uri[spos+1] == '.')) { + if(uri[spos+2] == '/') + spos += 2; + else + if((spos <= (l-3)) && + (uri[spos+2] == '.') && (uri[spos+3] == '/')) { + spos += 3; + while((tpos > 0) && (uri[--tpos] != '/')) + uri[tpos] = '\0'; + } else + uri[tpos++] = uri[spos++]; + } else { + if(uri[spos+1] != '/') + uri[tpos++] = uri[spos++]; + else + spos++; + } + } else + uri[tpos++] = uri[spos++]; + } + uri[tpos] = '\0'; +} + + +/* -------------------- util_uri_unescape_and_normalize -------------------- */ + +#ifdef XP_WIN32 +/* The server calls this function to unescape the URI and also normalize + * the uri. Normalizing the uri converts all "\" characters in the URI + * and pathinfo portion to "/". Does not touch "\" in query strings. + */ +void util_uri_unescape_and_normalize(char *s) +{ + char *t, *u; + + for(t = s, u = s; *t; ++t, ++u) { + if((*t == '%') && t[1] && t[2]) { + *u = ((t[1] >= 'A' ? ((t[1] & 0xdf) - 'A')+10 : (t[1] - '0'))*16) + + (t[2] >= 'A' ? ((t[2] & 0xdf) - 'A')+10 : (t[2] - '0')); + t += 2; + } + else + if(u != t) + *u = *t; + if (*u == '\\') /* normalize */ + *u = '/'; + } + *u = *t; +} +#endif /* XP_WIN32 */ + +/* -------------------------- util_uri_unescape --------------------------- */ + +NSAPI_PUBLIC void util_uri_unescape(char *s) +{ + char *t, *u; + + for(t = s, u = s; *t; ++t, ++u) { + if((*t == '%') && t[1] && t[2]) { + *u = ((t[1] >= 'A' ? ((t[1] & 0xdf) - 'A')+10 : (t[1] - '0'))*16) + + (t[2] >= 'A' ? ((t[2] & 0xdf) - 'A')+10 : (t[2] - '0')); + t += 2; + } + else + if(u != t) + *u = *t; + } + *u = *t; +} + + +/* --------------------------- util_uri_escape ---------------------------- */ + + +NSAPI_PUBLIC char *util_uri_escape(char *od, char *s) +{ + char *d; + + if(!od) + od = (char *) MALLOC((strlen(s)*3) + 1); + d = od; + + while(*s) { + if(strchr("% ?#:+&*\"<>\r\n", *s)) { + sprintf(d, "%%%2x", *s); + ++s; d += 3; + } + else + *d++ = *s++; + } + *d = '\0'; + return od; +} + + +/* --------------------------- util_url_escape ---------------------------- */ + + +NSAPI_PUBLIC char *util_url_escape(char *od, char *s) +{ + char *d; + + if(!od) + od = (char *) MALLOC((strlen(s)*3) + 1); + d = od; + + while(*s) { + if(strchr("% +*\"<>\r\n", *s)) { + sprintf(d, "%%%.2x", *s); + ++s; d += 3; + } + else + *d++ = *s++; + } + *d = '\0'; + return od; +} + + +/* ------------------------- util_mime_separator -------------------------- */ + + +NSAPI_PUBLIC int util_mime_separator(char *sep) +{ + srand(time(NULL)); + return util_sprintf(sep, "%c%c--%d%d%d", CR, LF, rand(), rand(), rand()); +} + + +/* ------------------------------ util_itoa ------------------------------- */ + + +/* + * Assumption: Reversing the digits will be faster in the general case + * than doing a log10 or some nasty trick to find the # of digits. + */ + +NSAPI_PUBLIC int util_itoa(int i, char *a) +{ + register int x, y, p; + register char c; + int negative; + + negative = 0; + if(i < 0) { + *a++ = '-'; + negative = 1; + i = -i; + } + p = 0; + while(i > 9) { + a[p++] = (i%10) + '0'; + i /= 10; + } + a[p++] = i + '0'; + + if(p > 1) { + for(x = 0, y = p - 1; x < y; ++x, --y) { + c = a[x]; + a[x] = a[y]; + a[y] = c; + } + } + a[p] = '\0'; + return p + negative; +} + + +/* ----------------------------- util_sprintf ----------------------------- */ + + +#include "prprf.h" + +/* + XXXrobm the NSPR interfaces don't allow me to just pass in a buffer + without a size + */ +#define UTIL_PRF_MAXSIZE 1048576 + +NSAPI_PUBLIC int util_vsnprintf(char *s, int n, register const char *fmt, + va_list args) +{ + return PR_vsnprintf(s, n, fmt, args); +} + +NSAPI_PUBLIC int util_snprintf(char *s, int n, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + return PR_vsnprintf(s, n, fmt, args); +} + +NSAPI_PUBLIC int util_vsprintf(char *s, register const char *fmt, va_list args) +{ + return PR_vsnprintf(s, UTIL_PRF_MAXSIZE, fmt, args); +} + +NSAPI_PUBLIC int util_sprintf(char *s, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + return PR_vsnprintf(s, UTIL_PRF_MAXSIZE, fmt, args); +} + +/* ---------------------------- util_sh_escape ---------------------------- */ + + +NSAPI_PUBLIC char *util_sh_escape(char *s) +{ + char *ns = (char *) MALLOC(strlen(s) * 2 + 1); /* worst case */ + register char *t, *u; + + for(t = s, u = ns; *t; ++t, ++u) { + if(strchr("&;`'\"|*?~<>^()[]{}$\\ #!", *t)) + *u++ = '\\'; + *u = *t; + } + *u = '\0'; + return ns; +} + +/* --------------------------- util_strcasecmp ---------------------------- */ + + +#ifdef NEED_STRCASECMP +/* These are stolen from mcom/lib/xp */ +NSAPI_PUBLIC +int util_strcasecmp(CASECMPARG_T char *one, CASECMPARG_T char *two) +{ + CASECMPARG_T char *pA; + CASECMPARG_T char *pB; + + for(pA=one, pB=two; *pA && *pB; pA++, pB++) + { + int tmp = tolower(*pA) - tolower(*pB); + if (tmp) + return tmp; + } + if (*pA) + return 1; + if (*pB) + return -1; + return 0; +} +#endif /* NEED_STRCASECMP */ + +#ifdef NEED_STRNCASECMP +NSAPI_PUBLIC +int util_strncasecmp(CASECMPARG_T char *one, CASECMPARG_T char *two, int n) +{ + CASECMPARG_T char *pA; + CASECMPARG_T char *pB; + + for(pA=one, pB=two;; pA++, pB++) + { + int tmp; + if (pA == one+n) + return 0; + if (!(*pA && *pB)) + return *pA - *pB; + tmp = tolower(*pA) - tolower(*pB); + if (tmp) + return tmp; + } +} +#endif /* NEED_STRNCASECMP */ + +#ifdef XP_WIN32 + + +/* util_delete_directory() + * This routine deletes all the files in a directory. If delete_directory is + * TRUE it will also delete the directory itself. + */ +VOID +util_delete_directory(char *FileName, BOOL delete_directory) +{ + HANDLE firstFile; + WIN32_FIND_DATA findData; + char *TmpFile, *NewFile; + + if (FileName == NULL) + return; + + TmpFile = (char *)MALLOC(strlen(FileName) + 5); + sprintf(TmpFile, "%s\\*.*", FileName); + firstFile = FindFirstFile(TmpFile, &findData); + FREE(TmpFile); + + if (firstFile == INVALID_HANDLE_VALUE) + return; + + if(strcmp(findData.cFileName, ".") && + strcmp(findData.cFileName, "..")) { + NewFile = (char *)MALLOC(strlen(FileName) + 1 + + strlen(findData.cFileName) + 1); + sprintf(NewFile, "%s\\%s",FileName, findData.cFileName); + DeleteFile(NewFile); + FREE(NewFile); + } + while (TRUE) { + if(!(FindNextFile(firstFile, &findData))) { + if (GetLastError() != ERROR_NO_MORE_FILES) { + ereport(LOG_WARN, XP_GetAdminStr(DBT_couldNotRemoveTemporaryDirectory_), FileName, GetLastError()); + } else { + FindClose(firstFile); + if (delete_directory) + if(!RemoveDirectory(FileName)) { + ereport(LOG_WARN, + XP_GetAdminStr(DBT_couldNotRemoveTemporaryDirectory_1), + FileName, GetLastError()); + } + return; + } + } else { + if(strcmp(findData.cFileName, ".") && + strcmp(findData.cFileName, "..")) { + NewFile = (char *)MALLOC(strlen(FileName) + 5 + + strlen(findData.cFileName) + 1); + sprintf(NewFile,"%s\\%s", FileName, findData.cFileName); + DeleteFile(NewFile); + FREE(NewFile); + } + } + } +} +#endif + +/* ------------------------------ util_strftime --------------------------- */ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strftime.c 5.11 (Berkeley) 2/24/91"; +#endif /* LIBC_SCCS and not lint */ + +#ifdef XP_UNIX +#include <sys/types.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#endif + +static char *afmt[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", +}; +static char *Afmt[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", + "Saturday", +}; + +static char *bfmt[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec", +}; +static char *Bfmt[] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December", +}; + +#define TM_YEAR_BASE 1900 + +static void _util_strftime_conv(char *, int, int, char); + +#define _util_strftime_add(str) for (;(*pt = *str++); pt++); +#define _util_strftime_copy(str, len) memcpy(pt, str, len); pt += len; +#define _util_strftime_fmt util_strftime + +/* util_strftime() + * This is an optimized version of strftime for speed. Avoids the thread + * unsafeness of BSD strftime calls. + */ +int +util_strftime(char *pt, const char *format, const struct tm *t) +{ + char *start = pt; + char *scrap; + + for (; *format; ++format) { + if (*format == '%') + switch(*++format) { + case 'a': /* abbreviated weekday name */ + *pt++ = afmt[t->tm_wday][0]; + *pt++ = afmt[t->tm_wday][1]; + *pt++ = afmt[t->tm_wday][2]; + continue; + case 'd': /* day of month */ + _util_strftime_conv(pt, t->tm_mday, 2, '0'); + pt += 2; + continue; + case 'S': + _util_strftime_conv(pt, t->tm_sec, 2, '0'); + pt += 2; + continue; + case 'M': + _util_strftime_conv(pt, t->tm_min, 2, '0'); + pt += 2; + continue; + case 'H': + _util_strftime_conv(pt, t->tm_hour, 2, '0'); + pt += 2; + continue; + case 'Y': + if (t->tm_year < 100) { + *pt++ = '1'; + *pt++ = '9'; + _util_strftime_conv(pt, t->tm_year, 2, '0'); + } else { + /* will fail after 2100; but who cares? */ + *pt++ = '2'; + *pt++ = '0'; + _util_strftime_conv(pt, t->tm_year-100, 2, '0'); + } + pt += 2; + continue; + case 'b': /* abbreviated month name */ + case 'h': + *pt++ = bfmt[t->tm_mon][0]; + *pt++ = bfmt[t->tm_mon][1]; + *pt++ = bfmt[t->tm_mon][2]; + continue; + case 'T': + case 'X': + pt += _util_strftime_fmt(pt, "%H:%M:%S", t); + continue; + case '\0': + --format; + break; + case 'A': + if (t->tm_wday < 0 || t->tm_wday > 6) + return(0); + scrap = Afmt[t->tm_wday]; + _util_strftime_add(scrap); + continue; + case 'B': + if (t->tm_mon < 0 || t->tm_mon > 11) + return(0); + scrap = Bfmt[t->tm_mon]; + _util_strftime_add(scrap); + continue; + case 'C': + pt += _util_strftime_fmt(pt, "%a %b %e %H:%M:%S %Y", t); + continue; + case 'c': + pt += _util_strftime_fmt(pt, "%m/%d/%y %H:%M:%S", t); + continue; + case 'D': + pt += _util_strftime_fmt(pt, "%m/%d/%y", t); + continue; + case 'e': + _util_strftime_conv(pt, t->tm_mday, 2, ' '); + pt += 2; + continue; + case 'I': + _util_strftime_conv(pt, t->tm_hour % 12 ? + t->tm_hour % 12 : 12, 2, '0'); + pt += 2; + continue; + case 'j': + _util_strftime_conv(pt, t->tm_yday + 1, 3, '0'); + pt += 3; + continue; + case 'k': + _util_strftime_conv(pt, t->tm_hour, 2, ' '); + pt += 2; + continue; + case 'l': + _util_strftime_conv(pt, t->tm_hour % 12 ? + t->tm_hour % 12 : 12, 2, ' '); + pt += 2; + continue; + case 'm': + _util_strftime_conv(pt, t->tm_mon + 1, 2, '0'); + pt += 2; + continue; + case 'n': + *pt = '\n'; + pt++; + continue; + case 'p': + if (t->tm_hour >= 12) { + *pt = 'P'; + pt++; + } else { + *pt = 'A'; + pt++; + } + *pt = 'M'; + pt++; + continue; + case 'R': + pt += _util_strftime_fmt(pt, "%H:%M", t); + continue; + case 'r': + pt += _util_strftime_fmt(pt, "%I:%M:%S %p", t); + continue; + case 't': + *pt = '\t'; + pt++; + continue; + case 'U': + _util_strftime_conv(pt, (t->tm_yday + 7 - t->tm_wday) / 7, + 2, '0'); + pt += 2; + continue; + case 'W': + _util_strftime_conv(pt, (t->tm_yday + 7 - + (t->tm_wday ? (t->tm_wday - 1) : 6)) + / 7, 2, '0'); + pt += 2; + continue; + case 'w': + _util_strftime_conv(pt, t->tm_wday, 1, '0'); + pt += 1; + continue; + case 'x': + pt += _util_strftime_fmt(pt, "%m/%d/%y", t); + continue; + case 'y': + _util_strftime_conv(pt, (t->tm_year + TM_YEAR_BASE) + % 100, 2, '0'); + pt += 2; + continue; + case '%': + /* + * X311J/88-090 (4.12.3.5): if conversion char is + * undefined, behavior is undefined. Print out the + * character itself as printf(3) does. + */ + default: + break; + } + *pt = *format; + pt++; + } + + start[pt-start] = '\0'; + + return pt - start; +} + +static void +_util_strftime_conv(char *pt, int n, int digits, char pad) +{ + static char buf[10]; + register char *p; + + if (n >= 100) { + p = buf + sizeof(buf)-2; + for (; n > 0 && p > buf; n /= 10, --digits) + *p-- = n % 10 + '0'; + while (p > buf && digits-- > 0) + *p-- = pad; + p++; + _util_strftime_add(p); + } else { + int tens; + int ones = n; + + tens = 0; + if ( ones >= 10 ) { + while ( ones >= 10 ) { + tens++; + ones = ones - 10; + } + *pt++ = '0'+tens; + digits--; + } + else + *pt++ = '0'; + *pt++ = '0'+ones; + digits--; + while(digits--) + *pt++ = pad; + } + return; +} + + +#ifdef XP_UNIX +/* + * Local Thread Safe version of waitpid. This prevents the process + * from blocking in the system call. + */ +NSAPI_PUBLIC pid_t +util_waitpid(pid_t pid, int *statptr, int options) +{ + pid_t rv; + + for(rv = 0; !rv; PR_Sleep(500)) { + rv = waitpid(pid, statptr, options | WNOHANG); + if (rv == -1) { + if (errno == EINTR) + rv = 0; /* sleep and try again */ + else + ereport(LOG_WARN, "waitpid failed for pid %d:%s", pid, system_errmsg()); + } + } + return rv; +} +#endif + +/* + * Various reentrant routines by mikep. See util.h and systems.h + */ + +/* + * These are only necessary if we turn on interrupts in NSPR + */ +#ifdef NEED_RELOCKS +#include "crit.h" +#define RE_LOCK(name) \ + static CRITICAL name##_crit = 0; \ + if (name##_crit == 0) name##_crit = crit_init(); \ + crit_enter(name##_crit) + +#define RE_UNLOCK(name) crit_exit(name##_crit) + +#else +#define RE_LOCK(name) /* nada */ +#define RE_UNLOCK(name) /* nil */ +#endif + + +NSAPI_PUBLIC char * +util_strtok(register char *s, + register const char *delim, + register char **lasts) +{ +#ifdef HAVE_STRTOK_R + return strtok_r(s, delim, lasts); +#else + /* + * THIS IS THE THREAD SAFE VERSION OF strtok captured from + * public NetBSD. Note that no locks are needed + */ + register char *spanp; + register int c, sc; + char *tok; + + if (s == NULL && (s = *lasts) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), + * sort of). + */ + +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *lasts = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), + * sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *lasts = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +#endif /* no strtok_r */ +} + +#ifndef XP_WIN32 +NSAPI_PUBLIC struct passwd * +util_getpwnam(const char *name, struct passwd *result, char *buffer, + int buflen) +{ +#ifdef HAVE_PW_R + +#ifdef AIX +#if OSVERSION >= 4320 + return ((int)getpwnam_r(name, result, buffer, buflen, 0) == 0 ? result : NULL); +#else + return ((int)getpwnam_r(name, result, buffer, buflen) == 0 ? result : NULL); +#endif +#else + return getpwnam_r(name, result, buffer, buflen); +#endif /* AIX */ + +#else + char *lastp; + struct passwd *r; + RE_LOCK(pw); + r = getpwnam(name); + if (!r) + return r; + + result->pw_gid = r->pw_gid; + result->pw_uid = r->pw_uid; + /* Hope this buffer is long enough */ + if (buffer) + util_snprintf(buffer, buflen, "%s:%s:%d:%d:%s:%s:%s", r->pw_name, r->pw_passwd, + r->pw_uid, r->pw_gid, r->pw_gecos, r->pw_dir, r->pw_shell); + RE_UNLOCK(pw); + + result->pw_name = util_strtok(buffer, ":", &lastp); + result->pw_passwd = util_strtok(NULL, ":", &lastp); + (void) util_strtok(NULL, ":", &lastp); + (void) util_strtok(NULL, ":", &lastp); + result->pw_gecos = util_strtok(NULL, ":", &lastp); + result->pw_dir = util_strtok(NULL, ":", &lastp); + result->pw_shell = util_strtok(NULL, ":", &lastp); + return result; +#endif +} +#endif + +NSAPI_PUBLIC struct tm * +util_localtime(const time_t *clock, struct tm *res) +{ +#ifdef HAVE_TIME_R + return localtime_r(clock, res); +#else + struct tm *rv; + time_t zero = 0x7fffffff; + + RE_LOCK(localtime); + RE_UNLOCK(localtime); + rv = localtime(clock); + if (!rv) + rv = localtime(&zero); + if (rv) + *res = *rv; + else + return NULL; + return res; +#endif +} + + +NSAPI_PUBLIC char * +util_ctime(const time_t *clock, char *buf, int buflen) +{ +/* + * From cgi-src/restore.c refering to XP_WIN32: + * MLM - gross, but it works, better now FLC + */ +#if !defined(HAVE_TIME_R) || defined(XP_WIN32) + RE_LOCK(ctime); + strncpy(buf, ctime(clock), buflen); + buf[buflen - 1] = '\0'; + RE_UNLOCK(ctime); + return buf; +#elif HAVE_TIME_R == 2 + return ctime_r(clock, buf); +#else /* HAVE_TIME_R == 3 */ + return ctime_r(clock, buf, buflen); +#endif +} + +NSAPI_PUBLIC struct tm * +util_gmtime(const time_t *clock, struct tm *res) +{ +#ifdef HAVE_TIME_R + return gmtime_r(clock, res); +#else + struct tm *rv; + time_t zero = 0x7fffffff; + + RE_LOCK(gmtime); + rv = gmtime(clock); + RE_UNLOCK(gmtime); + if (!rv) + rv = gmtime(&zero); + if (rv) + *res = *rv; + else + return NULL; + + return res; +#endif +} + +NSAPI_PUBLIC char * +util_asctime(const struct tm *tm, char *buf, int buflen) +{ +#if HAVE_TIME_R == 2 + return asctime_r(tm, buf); +#elif HAVE_TIME_R == 3 + return asctime_r(tm, buf, buflen); +#else + RE_LOCK(asctime); + strncpy(buf, asctime(tm), buflen); + buf[buflen - 1] = '\0'; + RE_UNLOCK(asctime); + return buf; +#endif +} + +NSAPI_PUBLIC char * +util_strerror(int errnum, char *msg, int buflen) +{ +#ifdef HAVE_STRERROR_R + /* More IBM real-genius */ + return ((int)strerror_r(errnum, msg, buflen) > 0) ? msg : NULL; +#else + /* RE_LOCK(strerror); I don't think this is worth the trouble */ + (void)strncpy(msg, strerror(errnum), buflen); + msg[buflen - 1] = '\0'; + return msg; + /* RE_UNLOCK(strerror); */ +#endif +} + + + +/* ------------------------------- OLD CODE ------------------------------- */ + + +#if 0 + +NSAPI_PUBLIC int util_vsnprintf(char *s, int n, register char *fmt, + va_list args) +{ + register int pos = 0, max = (n > 2 ? n-2 : -1), boundson; + register char c, *t; + + if((max == -1) && (n != -1)) + goto punt; + + boundson = (n != -1); + while(*fmt) { + if(boundson && (pos > max)) + break; + c = *fmt++; + switch(c) { + case '%': + switch(*fmt++) { + case 'd': + if(boundson && ((pos + 10) > max)) + goto punt; + pos += util_itoa(va_arg(args, int), &s[pos]); + break; + case 's': + t = va_arg(args, char *); + while(*t) { + s[pos++] = *t++; + if(boundson && (pos > max)) + goto punt; + } + break; + case 'c': + s[pos++] = (char) va_arg(args, int); + break; + case '%': + s[pos++] = '%'; + break; + } + break; + case '\\': + if( (s[pos++] = *fmt) ) + ++fmt; + break; + default: + s[pos++] = c; + } + } + punt: + s[pos] = '\0'; + + va_end(args); + return pos; +} + +NSAPI_PUBLIC int util_snprintf(char *s, int n, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + return util_vsnprintf(s, n, fmt, args); +} + +NSAPI_PUBLIC int util_vsprintf(char *s, register char *fmt, va_list args) +{ + return util_vsnprintf(s, -1, fmt, args); +} + +NSAPI_PUBLIC int util_sprintf(char *s, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + return util_vsnprintf(s, -1, fmt, args); +} +#endif diff --git a/lib/ldaputil/Makefile b/lib/ldaputil/Makefile new file mode 100644 index 00000000..c6c95400 --- /dev/null +++ b/lib/ldaputil/Makefile @@ -0,0 +1,65 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Makefile for libldapu.a (ldaputil library) +# +MCOM_ROOT=../../.. +MODULE=LibLdapUtil + +OBJDEST=$(OBJDIR)/lib/ldaputil + +include ../../nsconfig.mk + +ifeq ($(ARCH), WINNT) +LIBS=$(OBJDIR)/lib/libldapu.lib +MODULE_CFLAGS+= -DWINSOCK +else +LIBS=$(OBJDIR)/lib/libldapu.a +endif + +LOCAL_DEPS = $(LDAPSDK_DEP) + +MCC_INCLUDE=-I$(NSROOT)/include \ + -I$(MCOM_ROOT)/include \ + $(DBM_INCLUDE) $(LDAPSDK_INCLUDE) \ + $(SECURITY_INCLUDE) $(NSPR_INCLUDE) + +all: $(OBJDEST) $(LOCAL_DEPS) $(LIBS) + +$(OBJDEST): + mkdir -p $(OBJDEST) + +OSOBJS = + +OBJS=$(addprefix $(OBJDEST)/, \ + ldapauth.o \ + ldapdb.o \ + dbconf.o \ + certmap.o \ + cert.o \ + init.o \ + encode.o \ + errors.o \ + vtable.o \ + ) + +MODULE_CFLAGS+= $(TESTFLAGS) +MODULE_CFLAGS+= -DLDAPDB_THREAD_SAFE -I. + +ifeq ($(LDAP_NO_LIBLCACHE),1) +MODULE_CFLAGS+=-DNO_LIBLCACHE +endif + + +$(LIBS): $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ + +include $(INCLUDE_DEPENDS) + diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c new file mode 100644 index 00000000..34c41845 --- /dev/null +++ b/lib/ldaputil/cert.c @@ -0,0 +1,452 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include <malloc.h> + +/* removed for ns security integration +#include <sec.h> +*/ +#include "prmem.h" +#include "key.h" +#include "cert.h" +#include <ldaputil/certmap.h> +#include <ldaputil/errors.h> +#include <ldaputil/cert.h> +#include "ldaputili.h" + +NSAPI_PUBLIC int ldapu_get_cert (void *SSLendpoint, void **cert) +{ + /* TEMPORARY -- not implemented yet*/ + return LDAPU_FAILED; +} + + +NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + char *cert_subject = CERT_NameToAscii(&cert->subject); + + if (cert_subject != NULL) + *subjectDN = strdup(cert_subject); + else + *subjectDN=NULL; + + PR_Free(cert_subject); + return *subjectDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_SUBJECTDN_FAILED; +} + +NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert_in, char **issuerDN) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + char *cert_issuer = CERT_NameToAscii(&cert->issuer); + + *issuerDN = strdup(cert_issuer); + PR_Free(cert_issuer); + + return *issuerDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_ISSUERDN_FAILED; +} + +NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der, + unsigned int *len) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + SECItem derCert = ((CERTCertificate*)cert)->derCert; + unsigned char *data = derCert.data; + + *len = derCert.len; + *der = (unsigned char *)malloc(*len); + + if (!*der) return LDAPU_ERR_OUT_OF_MEMORY; + + memcpy(*der, data, *len); + + return *len ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_DERCERT_FAILED; +} + +static int certmap_name_to_secoid (const char *str) +{ + if (!ldapu_strcasecmp(str, "c")) return SEC_OID_AVA_COUNTRY_NAME; + if (!ldapu_strcasecmp(str, "o")) return SEC_OID_AVA_ORGANIZATION_NAME; + if (!ldapu_strcasecmp(str, "cn")) return SEC_OID_AVA_COMMON_NAME; + if (!ldapu_strcasecmp(str, "l")) return SEC_OID_AVA_LOCALITY; + if (!ldapu_strcasecmp(str, "st")) return SEC_OID_AVA_STATE_OR_PROVINCE; + if (!ldapu_strcasecmp(str, "ou")) return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME; + if (!ldapu_strcasecmp(str, "uid")) return SEC_OID_RFC1274_UID; + if (!ldapu_strcasecmp(str, "e")) return SEC_OID_PKCS9_EMAIL_ADDRESS; + if (!ldapu_strcasecmp(str, "mail")) return SEC_OID_RFC1274_MAIL; + if (!ldapu_strcasecmp(str, "dc")) return SEC_OID_AVA_DC; + + return SEC_OID_AVA_UNKNOWN; /* return invalid OID */ +} + +NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn, + const char *attr, char ***val_out) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + CERTName *cert_dn; + CERTRDN **rdns; + CERTRDN **rdn; + CERTAVA **avas; + CERTAVA *ava; + int attr_tag = certmap_name_to_secoid(attr); + char **val; + char **ptr; + int rv; + + *val_out = 0; + + if (attr_tag == SEC_OID_AVA_UNKNOWN) { + return LDAPU_ERR_INVALID_ARGUMENT; + } + + if (which_dn == LDAPU_SUBJECT_DN) + cert_dn = &cert->subject; + else if (which_dn == LDAPU_ISSUER_DN) + cert_dn = &cert->issuer; + else + return LDAPU_ERR_INVALID_ARGUMENT; + + val = (char **)malloc(32*sizeof(char *)); + + if (!val) return LDAPU_ERR_OUT_OF_MEMORY; + + ptr = val; + + rdns = cert_dn->rdns; + + if (rdns) { + for (rdn = rdns; *rdn; rdn++) { + avas = (*rdn)->avas; + while ((ava = *avas++) != NULL) { + int tag = CERT_GetAVATag(ava); + + if (tag == attr_tag) { + char buf[BIG_LINE]; + int lenLen; + int vallen; + /* Found it */ + + /* Copied from ns/lib/libsec ... + * XXX this code is incorrect in general + * -- should use a DER template. + */ + lenLen = 2; + if (ava->value.len >= 128) lenLen = 3; + vallen = ava->value.len - lenLen; + + rv = CERT_RFC1485_EscapeAndQuote(buf, + BIG_LINE, + (char*) ava->value.data + lenLen, + vallen); + + if (rv == SECSuccess) { + *ptr++ = strdup(buf); + } + break; + } + } + } + } + + *ptr = 0; + + if (*val) { + /* At least one value found */ + *val_out = val; + rv = LDAPU_SUCCESS; + } + else { + free(val); + rv = LDAPU_FAILED; + } + + return rv; +} + +static void +_rdns_free (char*** rdns) +{ + auto char*** rdn; + for (rdn = rdns; *rdn; ++rdn) { + ldap_value_free (*rdn); + } + free (rdns); +} + +static char*** +_explode_dn (const char* dn) +{ + auto char*** exp = NULL; + if (dn && *dn) { + auto char** rdns = ldap_explode_dn (dn, 0); + if (rdns) { + auto size_t expLen = 0; + auto char** rdn; + for (rdn = rdns; *rdn; ++rdn) { + auto char** avas = ldap_explode_rdn (*rdn, 0); + if (avas && *avas) { + exp = (char***) ldapu_realloc (exp, sizeof(char**) * (expLen + 2)); + if (exp) { + exp[expLen++] = avas; + } else { + ldap_value_free (avas); + break; + } + } else { /* parse error */ + if (avas) { + ldap_value_free (avas); + } + if (exp) { + exp[expLen] = NULL; + _rdns_free (exp); + exp = NULL; + } + break; + } + } + if (exp) { + exp[expLen] = NULL; + } + ldap_value_free (rdns); + } + } + return exp; +} + +static size_t +_rdns_count (char*** rdns) +{ + auto size_t count = 0; + auto char*** rdn; + for (rdn = rdns; *rdns; ++rdns) { + auto char** ava; + for (ava = *rdns; *ava; ++ava) { + ++count; + } + } + return count; +} + +static int +_replaceAVA (char* attr, char** avas) +{ + if (attr && avas) { + for (; *avas; ++avas) { + if (!ldapu_strcasecmp (*avas, attr)) { + *avas = attr; + return 1; + } + } + } + return 0; +} + +struct _attr_getter_pair { + char* (*getter) (CERTName* dn); + const char* name1; + const char* name2; +} _attr_getter_table[] = +{ + {NULL, "OU", "organizationalUnitName"}, + {CERT_GetOrgName, "O", "organizationName"}, + {CERT_GetCommonName, "CN", "commonName"}, + {CERT_GetCertEmailAddress, "E", NULL}, + {CERT_GetCertEmailAddress, "MAIL", "rfc822mailbox"}, + {CERT_GetCertUid, "uid", NULL}, + {CERT_GetCountryName, "C", "country"}, + {CERT_GetStateName, "ST", "state"}, + {CERT_GetLocalityName, "L", "localityName"}, + {CERT_GetDomainComponentName, "DC", "dc"}, + {NULL, NULL, NULL} +}; + +static int +_is_OU (const char* attr) +{ + auto struct _attr_getter_pair* descAttr; + for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) { + if (descAttr->getter == NULL) { /* OU attribute */ + if (!ldapu_strcasecmp (attr, descAttr->name1) || (descAttr->name2 && + !ldapu_strcasecmp (attr, descAttr->name2))) { + return 1; + } + break; + } + } + return 0; +} + +static char** +_previous_OU (char** ava, char** avas) +{ + while (ava != avas) { + --ava; + if (_is_OU (*ava)) { + return ava; + } + } + return NULL; +} + +static char* +_value_normalize (char* value) + /* Remove leading and trailing spaces, and + change consecutive spaces to a single space. + */ +{ + auto char* t; + auto char* f; + t = f = value; + while (*f == ' ') ++f; /* ignore leading spaces */ + for (; *f; ++f) { + if (*f != ' ' || t[-1] != ' ') { + *t++ = *f; /* no consecutive spaces */ + } + } + if (t > value && t[-1] == ' ') { + --t; /* ignore trailing space */ + } + *t = '\0'; + return value; +} + +static int +_explode_AVA (char* AVA) + /* Change an attributeTypeAndValue a la <draft-ietf-asid-ldapv3-dn>, + to the type name, followed immediately by the attribute value, + both normalized. + */ +{ + auto char* value = strchr (AVA, '='); + if (!value) return LDAPU_FAILED; + *value++ = '\0'; + _value_normalize (AVA); + _value_normalize (value); + { + auto char* typeEnd = AVA + strlen (AVA); + if ((typeEnd + 1) != value) { + memmove (typeEnd+1, value, strlen(value)+1); + } + } + return LDAPU_SUCCESS; +} + +static char* +_AVA_value (char* AVA) +{ + return (AVA + strlen (AVA) + 1); +} + +static int +_value_match (char* value, char* desc) +{ + auto const int result = + !ldapu_strcasecmp (_value_normalize(value), desc); + return result; +} + +int +ldapu_member_certificate_match (void* cert, const char* desc) +/* + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS cert matches desc + * LDAPU_FAILED cert doesn't match desc + * <rv> Something went wrong. + */ +{ + auto int err = LDAPU_FAILED; + auto char*** descRDNs; + if (!cert || !desc || desc[0] != '{') return LDAPU_FAILED; + if (desc[1] == '\0') return LDAPU_SUCCESS; /* no AVAs */ + descRDNs = _explode_dn (desc+1); + if (descRDNs) { + auto char** descAVAs = (char**)ldapu_malloc(sizeof(char*) * (_rdns_count(descRDNs)+1)); + if (!descAVAs) { + err = LDAPU_ERR_OUT_OF_MEMORY; + } else { + auto CERTName* subject = &(((CERTCertificate*)cert)->subject); + auto char** descAVA; + + err = LDAPU_SUCCESS; + { /* extract all the AVAs, but not duplicate types, except OU */ + auto size_t descAVAsLen = 0; + auto char*** descRDN; + descAVAs[0] = NULL; + for (descRDN = descRDNs; err == LDAPU_SUCCESS && *descRDN; ++descRDN) { + for (descAVA = *descRDN; err == LDAPU_SUCCESS && *descAVA; ++descAVA) { + err = _explode_AVA (*descAVA); + if (err == LDAPU_SUCCESS) { + if (_is_OU (*descAVA) || + !_replaceAVA (*descAVA, descAVAs)) { + descAVAs[descAVAsLen++] = *descAVA; + descAVAs[descAVAsLen] = NULL; + } + } + } + } + } + + /* match all the attributes except OU */ + for (descAVA = descAVAs; err == LDAPU_SUCCESS && *descAVA; ++descAVA) { + auto struct _attr_getter_pair* descAttr; + err = LDAPU_FAILED; /* if no match */ + for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) { + if (!ldapu_strcasecmp (*descAVA, descAttr->name1) || (descAttr->name2 && + !ldapu_strcasecmp (*descAVA, descAttr->name2))) { + if (descAttr->getter == NULL) { /* OU attribute */ + err = LDAPU_SUCCESS; /* for now */ + } else { + auto char* certVal = (*(descAttr->getter))(subject); + if (certVal && _value_match (certVal, _AVA_value (*descAVA))) { + err = LDAPU_SUCCESS; + } + PR_Free (certVal); + } + break; + } + } + } + + /* match the OU attributes */ + if (err == LDAPU_SUCCESS && descAVA != descAVAs) { + /* Iterate over the OUs in the certificate subject */ + auto CERTRDN** certRDN = subject->rdns; + descAVA = _previous_OU (descAVA, descAVAs); + for (; descAVA && *certRDN; ++certRDN) { + auto CERTAVA** certAVA = (*certRDN)->avas; + for (; descAVA && *certAVA; ++certAVA) { + auto const int tag = CERT_GetAVATag (*certAVA); + if (tag == SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME) { + auto const size_t certValLen =(*certAVA)->value.len; + auto const size_t lenLen = (certValLen < 128) ? 2 : 3; + auto const size_t buflen = certValLen - lenLen; + auto char* buf = (char*)ldapu_malloc(buflen+1); + if (!buf) { + err = LDAPU_ERR_OUT_OF_MEMORY; + descAVA = NULL; + } else { + memcpy (buf, (*certAVA)->value.data+lenLen, buflen); + buf[buflen] = 0; + if (_value_match (buf, _AVA_value (*descAVA))) { + descAVA = _previous_OU (descAVA, descAVAs); + } + free (buf); + } + } + } + } + if (descAVA) { + err = LDAPU_FAILED; /* no match for descAVA in subject */ + } + } + free (descAVAs); + } + _rdns_free (descRDNs); + } + return err; +} + diff --git a/lib/ldaputil/certmap.c b/lib/ldaputil/certmap.c new file mode 100644 index 00000000..c2c718fc --- /dev/null +++ b/lib/ldaputil/certmap.c @@ -0,0 +1,1950 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <malloc.h> + +/* removed for ns security integration +#include <sec.h> +*/ +#include <plstr.h> +#include <prlink.h> +#include <key.h> +#include <cert.h> +#include <ldaputil/certmap.h> +#include <ldaputil/ldapauth.h> +#include <ldaputil/errors.h> +#include <ldaputil/ldaputil.h> +#include "ldaputili.h" + +#ifndef BIG_LINE +#define BIG_LINE 1024 +#endif + +/* This is hack, the function is defined in cert/alg1485.c */ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +static char this_dllname[256]; +static const char *LIB_DIRECTIVE = "certmap"; +static const int LIB_DIRECTIVE_LEN = 7; /* strlen("LIB_DIRECTIVE") */ + +static char *ldapu_dn_normalize( char *dn ); + +typedef struct { + FILE *fp; + void *arg; +} LDAPUPrintInfo_t; + +static LDAPUCertMapListInfo_t *certmap_listinfo = 0; +static LDAPUCertMapInfo_t *default_certmap_info = 0; + +static const char *certmap_attrs [] = { + 0, + 0, + 0, + 0 +}; + +const long CERTMAP_BIT_POS_UNKNOWN = 0; /* unknown OID */ +const long CERTMAP_BIT_POS_CN = 1L << 1; /* Common Name */ +const long CERTMAP_BIT_POS_OU = 1L << 2; /* Organization unit */ +const long CERTMAP_BIT_POS_O = 1L << 3; /* Organization */ +const long CERTMAP_BIT_POS_C = 1L << 4; /* Country */ +const long CERTMAP_BIT_POS_L = 1L << 5; /* Locality */ +const long CERTMAP_BIT_POS_ST = 1L << 6; /* State or Province */ +const long CERTMAP_BIT_POS_MAIL = 1L << 7; /* E-mail Address */ +const long CERTMAP_BIT_POS_UID = 1L << 8; /* UID */ +const long CERTMAP_BIT_POS_DC = 1L << 9; /* DC */ + +const int SEC_OID_AVA_UNKNOWN = 0; /* unknown OID */ + +static long certmap_secoid_to_bit_pos (int oid) +{ + switch(oid) { + case SEC_OID_AVA_COUNTRY_NAME: return CERTMAP_BIT_POS_C; + case SEC_OID_AVA_ORGANIZATION_NAME: return CERTMAP_BIT_POS_O; + case SEC_OID_AVA_COMMON_NAME: return CERTMAP_BIT_POS_CN; + case SEC_OID_AVA_LOCALITY: return CERTMAP_BIT_POS_L; + case SEC_OID_AVA_STATE_OR_PROVINCE: return CERTMAP_BIT_POS_ST; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: return CERTMAP_BIT_POS_OU; + case SEC_OID_RFC1274_UID: return CERTMAP_BIT_POS_UID; + /* Map "E" and "MAIL" to the same bit position */ + case SEC_OID_PKCS9_EMAIL_ADDRESS: return CERTMAP_BIT_POS_MAIL; + case SEC_OID_RFC1274_MAIL: return CERTMAP_BIT_POS_MAIL; + case SEC_OID_AVA_DC: return CERTMAP_BIT_POS_DC; + default: return CERTMAP_BIT_POS_UNKNOWN; + } +} + +static const char *certmap_secoid_to_name (int oid) +{ + switch(oid) { + case SEC_OID_AVA_COUNTRY_NAME: return "C"; + case SEC_OID_AVA_ORGANIZATION_NAME: return "O"; + case SEC_OID_AVA_COMMON_NAME: return "CN"; + case SEC_OID_AVA_LOCALITY: return "L"; + case SEC_OID_AVA_STATE_OR_PROVINCE: return "ST"; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: return "OU"; + case SEC_OID_RFC1274_UID: return "UID"; + /* Map both 'e' and 'mail' to 'mail' in LDAP */ + case SEC_OID_PKCS9_EMAIL_ADDRESS: return "MAIL"; + case SEC_OID_RFC1274_MAIL: return "MAIL"; + case SEC_OID_AVA_DC: return "DC"; + default: return 0; + } +} + +static void tolower_string (char *str) +{ + if (str) { + while (*str) { + *str = tolower(*str); + str++; + } + } +} + +static long certmap_name_to_bit_pos (const char *str) +{ + if (!ldapu_strcasecmp(str, "c")) return CERTMAP_BIT_POS_C; + if (!ldapu_strcasecmp(str, "o")) return CERTMAP_BIT_POS_O; + if (!ldapu_strcasecmp(str, "cn")) return CERTMAP_BIT_POS_CN; + if (!ldapu_strcasecmp(str, "l")) return CERTMAP_BIT_POS_L; + if (!ldapu_strcasecmp(str, "st")) return CERTMAP_BIT_POS_ST; + if (!ldapu_strcasecmp(str, "ou")) return CERTMAP_BIT_POS_OU; + if (!ldapu_strcasecmp(str, "uid")) return CERTMAP_BIT_POS_UID; + /* Map "E" and "MAIL" to the same bit position */ + if (!ldapu_strcasecmp(str, "e")) return CERTMAP_BIT_POS_MAIL; + if (!ldapu_strcasecmp(str, "mail")) return CERTMAP_BIT_POS_MAIL; + if (!ldapu_strcasecmp(str, "dc")) return CERTMAP_BIT_POS_DC; + + return CERTMAP_BIT_POS_UNKNOWN; +} + +static int certmap_name_to_secoid (const char *str) +{ + if (!ldapu_strcasecmp(str, "c")) return SEC_OID_AVA_COUNTRY_NAME; + if (!ldapu_strcasecmp(str, "o")) return SEC_OID_AVA_ORGANIZATION_NAME; + if (!ldapu_strcasecmp(str, "cn")) return SEC_OID_AVA_COMMON_NAME; + if (!ldapu_strcasecmp(str, "l")) return SEC_OID_AVA_LOCALITY; + if (!ldapu_strcasecmp(str, "st")) return SEC_OID_AVA_STATE_OR_PROVINCE; + if (!ldapu_strcasecmp(str, "ou")) return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME; + if (!ldapu_strcasecmp(str, "uid")) return SEC_OID_RFC1274_UID; + if (!ldapu_strcasecmp(str, "e")) return SEC_OID_PKCS9_EMAIL_ADDRESS; + if (!ldapu_strcasecmp(str, "mail")) return SEC_OID_RFC1274_MAIL; + if (!ldapu_strcasecmp(str, "dc")) return SEC_OID_AVA_DC; + + return SEC_OID_AVA_UNKNOWN; /* return invalid OID */ +} + +NSAPI_PUBLIC int ldapu_list_alloc (LDAPUList_t **list) +{ + *list = (LDAPUList_t *)malloc(sizeof(LDAPUList_t)); + + if (!*list) return LDAPU_ERR_OUT_OF_MEMORY; + + memset((void *)*list, 0, sizeof(LDAPUList_t)); + return LDAPU_SUCCESS; +} + +static int ldapu_list_add_node (LDAPUList_t *list, LDAPUListNode_t *node) +{ + if (list->head) { + node->prev = list->tail; + list->tail->next = node; + } + else { + node->prev = 0; + list->head = node; + } + + node->next = 0; + list->tail = node; + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int ldapu_list_add_info (LDAPUList_t *list, void *info) +{ + LDAPUListNode_t *node; + + /* Allocate the list node and set info in the node. */ + node = (LDAPUListNode_t *)malloc(sizeof(LDAPUListNode_t)); + + if (!node) { + return LDAPU_ERR_OUT_OF_MEMORY; + } + + memset((void *)node, 0, sizeof(LDAPUListNode_t)); + node->info = info; + + return ldapu_list_add_node(list, node); +} + +static int ldapu_list_remove_node (LDAPUList_t *list, LDAPUListNode_t *node) +{ + if (list->head == node) { + list->head = node->next; + if (list->tail == node) list->tail = 0; /* removed the only node */ + } + else if (list->tail == node) { + list->tail = node->prev; + } + else { + node->prev->next = node->next; + node->next->prev = node->prev; + } + + node->next = 0; + node->prev = 0; + return LDAPU_SUCCESS; +} + +static int ldapu_list_copy (const LDAPUList_t *from, LDAPUList_t **to, + LDAPUListNodeFn_t copy_fn) +{ + LDAPUListNode_t *node = from->head; + LDAPUListNode_t *newnode; + LDAPUList_t *list; + int rv; + + *to = 0; + rv = ldapu_list_alloc(&list); + if (rv != LDAPU_SUCCESS) return rv; + + while(node) { + newnode = (LDAPUListNode_t *)(*copy_fn)(node->info, 0); + if (!newnode) return LDAPU_ERR_OUT_OF_MEMORY; + rv = ldapu_list_add_info(list, newnode); + if (rv != LDAPU_SUCCESS) return rv; + node = node->next; + } + + *to = list; + return LDAPU_SUCCESS; +} + +static int ldapu_list_find_node (const LDAPUList_t *list, + LDAPUListNode_t **found, + LDAPUListNodeFn_t find_fn, + void *find_arg) +{ + LDAPUListNode_t *node = list->head; + + while(node) { + if ((*find_fn)(node->info, find_arg) == LDAPU_SUCCESS) { + *found = node; + return LDAPU_SUCCESS; + } + node = node->next; + } + + return LDAPU_ERR_CERTMAP_INFO_MISSING; +} + +static int ldapu_list_print (LDAPUList_t *list, LDAPUListNodeFn_t print_fn, + LDAPUPrintInfo_t *pinfo) +{ + LDAPUListNode_t *node = list->head; + int rv; + + while(node) { + rv = (int)(*print_fn)(node->info, pinfo); + if (rv != LDAPU_SUCCESS) return rv; + node = node->next; + } + + return LDAPU_SUCCESS; +} + + +static void ldapu_list_free (LDAPUList_t *list, LDAPUListNodeFn_t free_fn) +{ + if (list) { + auto LDAPUListNode_t *node = list->head; + while (node) { + auto LDAPUListNode_t *next = node->next; + if (free_fn) { + (*free_fn)(node->info, 0); + node->info = 0; + } + node->info = 0; + free(node); + node = next; + } + list->head = 0; + list->tail = 0; + } + return; +} + +NSAPI_PUBLIC int ldapu_propval_alloc (const char *prop, const char *val, + LDAPUPropVal_t **propval) +{ + *propval = (LDAPUPropVal_t *)malloc(sizeof(LDAPUPropVal_t)); + + if (!*propval) return LDAPU_ERR_OUT_OF_MEMORY; + + (*propval)->prop = prop ? strdup(prop) : 0; + (*propval)->val = val ? strdup(val) : 0; + + if ((!prop || (*propval)->prop) && (!val || (*propval)->val)) { + /* strdup worked */ + return LDAPU_SUCCESS; + } + else { + return LDAPU_ERR_OUT_OF_MEMORY; + } +} + +static void *ldapu_propval_copy (void *info, void *arg) +{ + LDAPUPropVal_t *propval = (LDAPUPropVal_t *)info; + LDAPUPropVal_t *copy = 0; + int rv; + + rv = ldapu_propval_alloc(propval->prop, propval->val, ©); + + if (rv != LDAPU_SUCCESS) return 0; + return copy; +} + +#define PRINT_STR(x) (x ? x : "<NULL>") + +static void * ldapu_propval_print (void *info, void *arg) +{ + LDAPUPropVal_t *propval = (LDAPUPropVal_t *)info; + LDAPUPrintInfo_t *pinfo = (LDAPUPrintInfo_t *)arg; + + if (!pinfo || !pinfo->fp) { + fprintf(stderr, "\tprop = \"%s\", \tval = \"%s\"\n", + PRINT_STR(propval->prop), + PRINT_STR(propval->val)); + } + else { + char *issuerName = (char *)pinfo->arg; + + fprintf(pinfo->fp, "%s:%s %s\n", issuerName, + propval->prop ? propval->prop : "", + propval->val ? propval->val : ""); + } + + return 0; +} + +static int PresentInComps (long comps_bitmask, int tag) +{ + long bit = certmap_secoid_to_bit_pos(tag); + + if (comps_bitmask & bit) + return 1; + else + return 0; +} + +static void print_oid_bitmask (long bitmask) +{ + fprintf(stderr, "%x: ", bitmask); + + if (PresentInComps(bitmask, SEC_OID_AVA_COUNTRY_NAME)) + fprintf(stderr, " C"); + if (PresentInComps(bitmask, SEC_OID_AVA_ORGANIZATION_NAME)) + fprintf(stderr, " O"); + if (PresentInComps(bitmask, SEC_OID_AVA_COMMON_NAME)) + fprintf(stderr, " CN"); + if (PresentInComps(bitmask, SEC_OID_AVA_LOCALITY)) + fprintf(stderr, " L"); + if (PresentInComps(bitmask, SEC_OID_AVA_STATE_OR_PROVINCE)) + fprintf(stderr, " ST"); + if (PresentInComps(bitmask, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME)) + fprintf(stderr, " OU"); + if (PresentInComps(bitmask, SEC_OID_PKCS9_EMAIL_ADDRESS)) + fprintf(stderr, " E"); + if (PresentInComps(bitmask, SEC_OID_RFC1274_UID)) + fprintf(stderr, " UID"); + if (PresentInComps(bitmask, SEC_OID_RFC1274_MAIL)) + fprintf(stderr, " MAIL"); + if (PresentInComps(bitmask, SEC_OID_AVA_DC)) + fprintf(stderr, " DC"); + /* check for not yet known oid */ + if (PresentInComps(bitmask, 34325)) + fprintf(stderr, " UNKNOWN"); + + fprintf(stderr, "\n"); +} + +static void *ldapu_certinfo_print (void *info, void *arg) +{ + LDAPUCertMapInfo_t *certinfo = (LDAPUCertMapInfo_t*)info; + LDAPUPrintInfo_t *pinfo = (LDAPUPrintInfo_t *)arg; + + if (!certinfo) return (void *)LDAPU_ERR_WRONG_ARGS; + + if (!pinfo || !pinfo->fp) { + fprintf(stderr, "Printing cert mapinfo: \"%s\" ...\n", + PRINT_STR(certinfo->issuerName)); + fprintf(stderr, "\tissuerDN = \"%s\"\n", + PRINT_STR(certinfo->issuerDN)); + fprintf(stderr, "\tParsed dncomps: "); + print_oid_bitmask(certinfo->dncomps); + fprintf(stderr, "\tParsed filtercomps: "); + print_oid_bitmask(certinfo->filtercomps); + + if (certinfo->propval) { + fprintf(stderr, "\tPrinting propval pairs: ...\n"); + if (certinfo->propval) + ldapu_list_print(certinfo->propval, ldapu_propval_print, pinfo); + } + else { + fprintf(stderr, "\tNo propval pairs\n"); + } + } + else { + LDAPUPrintInfo_t pinfo2; + + pinfo2.fp = pinfo->fp; + pinfo2.arg = certinfo->issuerName; + + /* Write certinfo to pinfo->fp */ + fprintf(pinfo->fp, "%s %s %s\n", LIB_DIRECTIVE, certinfo->issuerName, + certinfo->issuerDN ? certinfo->issuerDN : ""); + if (certinfo->propval) + ldapu_list_print(certinfo->propval, ldapu_propval_print, &pinfo2); + fprintf(pinfo->fp, "\n"); + } + + return (void *)LDAPU_SUCCESS; +} + +static int dbconf_to_certmap_err (int err) +{ + switch(err) { + case LDAPU_ERR_DBNAME_IS_MISSING: + return LDAPU_ERR_CANAME_IS_MISSING; + case LDAPU_ERR_PROP_IS_MISSING: + return LDAPU_ERR_CAPROP_IS_MISSING; + default: + return err; + } +} + +/* CAUTION: this function hijacks some substructures from db_info and make + * the pointers to it NULL in the db_info. It is safe to deallocate db_info. + */ +static int dbinfo_to_certinfo (DBConfDBInfo_t *db_info, + LDAPUCertMapInfo_t **certinfo_out) +{ + LDAPUCertMapInfo_t *certinfo; + int rv; + + *certinfo_out = 0; + + certinfo = (LDAPUCertMapInfo_t *)malloc(sizeof(LDAPUCertMapInfo_t)); + + if (!certinfo) return LDAPU_ERR_OUT_OF_MEMORY; + + memset((void *)certinfo, 0, sizeof(LDAPUCertMapInfo_t)); + + /* hijack few structures rather then copy. Make the pointers to the + structures NULL in the original structure so that they don't freed up + when db_info is freed. */ + certinfo->issuerName = db_info->dbname; + db_info->dbname = 0; + + certinfo->issuerDN = ldapu_dn_normalize(db_info->url); + db_info->url = 0; + + /* hijack actual prop-vals from dbinfo -- to avoid strdup calls */ + if (db_info->firstprop) { + LDAPUPropValList_t *propval_list; + LDAPUPropVal_t *propval; + DBPropVal_t *dbpropval; + + dbpropval = db_info->firstprop; + + rv = ldapu_list_alloc(&propval_list); + + if (rv != LDAPU_SUCCESS) return rv; + + while(dbpropval) { + propval = (LDAPUPropVal_t *)malloc(sizeof(LDAPUPropVal_t)); + + if (!propval) { + free(certinfo); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + propval->prop = dbpropval->prop; + dbpropval->prop = 0; + + propval->val = dbpropval->val; + dbpropval->val = 0; + + rv = ldapu_list_add_info(propval_list, propval); + + if (rv != LDAPU_SUCCESS) { + free(certinfo); + return rv; + } + + dbpropval = dbpropval->next; + } + + certinfo->propval = propval_list; + } + + *certinfo_out = certinfo; + + return LDAPU_SUCCESS; +} + +static int ldapu_binary_cmp_certs (void *subject_cert, + void *entry_cert_binary, + unsigned long entry_cert_len) +{ + SECItem derCert = ((CERTCertificate*)subject_cert)->derCert; + int rv; + + /* binary compare the two certs */ + if (derCert.len == entry_cert_len && + !memcmp(derCert.data, entry_cert_binary, entry_cert_len)) + { + rv = LDAPU_SUCCESS; + } + else { + rv = LDAPU_ERR_CERT_VERIFY_FAILED; + } + + return rv; +} + + +static int ldapu_cert_verifyfn_default (void *subject_cert, LDAP *ld, + void *certmap_info, LDAPMessage *res, + LDAPMessage **entry_out) +{ + LDAPMessage *entry; + struct berval **bvals; + int i; + int rv = LDAPU_ERR_CERT_VERIFY_FAILED; + char *cert_attr = ldapu_strings[LDAPU_STR_ATTR_CERT]; + char *cert_attr_nosubtype = ldapu_strings[LDAPU_STR_ATTR_CERT_NOSUBTYPE]; + + *entry_out = 0; + + for (entry = ldapu_first_entry(ld, res); entry != NULL; + entry = ldapu_next_entry(ld, entry)) + { + if (((bvals = ldapu_get_values_len(ld, entry, cert_attr)) == NULL) && + ((bvals = ldapu_get_values_len(ld, entry, cert_attr_nosubtype)) == NULL)) { + rv = LDAPU_ERR_CERT_VERIFY_NO_CERTS; + continue; + } + + for ( i = 0; bvals[i] != NULL; i++ ) { + rv = ldapu_binary_cmp_certs (subject_cert, + bvals[i]->bv_val, + bvals[i]->bv_len); + + if (rv == LDAPU_SUCCESS) { + break; + } + } + + ldapu_value_free_len(ld, bvals); + + if (rv == LDAPU_SUCCESS) { + *entry_out = entry; + break; + } + } + + return rv; +} + +static int parse_into_bitmask (const char *comps_in, long *bitmask_out, + long default_val) +{ + long bitmask; + char *comps = comps_in ? strdup(comps_in) : 0; + + if (!comps) { + /* Not present in the config file */ + bitmask = default_val; + } + else if (!*comps) { + /* present but empty */ + bitmask = 0; + } + else { + char *ptr = comps; + char *name = comps; + long bit; + int break_loop = 0; + + bitmask = 0; + + while (*name) { + /* advance ptr to delimeter */ + while(*ptr && !isspace(*ptr) && *ptr != ',') ptr++; + + if (!*ptr) + break_loop = 1; + else + *ptr++ = 0; + + bit = certmap_name_to_bit_pos(name); + bitmask |= bit; + + if (break_loop) break; + /* skip delimeters */ + while(*ptr && (isspace(*ptr) || *ptr == ',')) ptr++; + name = ptr; + } + } + + if (comps) free(comps); + *bitmask_out = bitmask; +/* print_oid_bitmask(bitmask); */ + return LDAPU_SUCCESS; +} + +static int process_certinfo (LDAPUCertMapInfo_t *certinfo) +{ + int rv = LDAPU_SUCCESS; + char *dncomps = 0; + char *filtercomps = 0; + char *libname = 0; + char *verify = 0; + char *fname = 0; + char *searchAttr = 0; + + if (!ldapu_strcasecmp(certinfo->issuerName, "default")) { + default_certmap_info = certinfo; + } + else if (!certinfo->issuerDN) { + return LDAPU_ERR_NO_ISSUERDN_IN_CONFIG_FILE; + } + else { + rv = ldapu_list_add_info(certmap_listinfo, certinfo); + } + + if (rv != LDAPU_SUCCESS) return rv; + + /* look for dncomps property and parse it into the dncomps bitmask */ + rv = ldapu_certmap_info_attrval (certinfo, LDAPU_ATTR_DNCOMPS, &dncomps); + + if (rv == LDAPU_SUCCESS && dncomps) { + certinfo->dncompsState = COMPS_HAS_ATTRS; + tolower_string(dncomps); + } + else if (rv == LDAPU_FAILED) { + certinfo->dncompsState = COMPS_COMMENTED_OUT; + rv = LDAPU_SUCCESS; + } + else if (rv == LDAPU_SUCCESS && !dncomps) { + certinfo->dncompsState = COMPS_EMPTY; + dncomps = ""; /* present but empty */ + } + + rv = parse_into_bitmask (dncomps, &certinfo->dncomps, -1); + + if (dncomps && *dncomps) free(dncomps); + + if (rv != LDAPU_SUCCESS) return rv; + + /* look for filtercomps property and parse it into the filtercomps bitmask */ + rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_FILTERCOMPS, + &filtercomps); + + if (rv == LDAPU_SUCCESS && filtercomps) { + certinfo->filtercompsState = COMPS_HAS_ATTRS; + tolower_string(filtercomps); + } + else if (rv == LDAPU_FAILED) { + certinfo->filtercompsState = COMPS_COMMENTED_OUT; + rv = LDAPU_SUCCESS; + } + else if (rv == LDAPU_SUCCESS && !filtercomps) { + certinfo->filtercompsState = COMPS_EMPTY; + filtercomps = ""; /* present but empty */ + } + + rv = parse_into_bitmask (filtercomps, &certinfo->filtercomps, 0); + + if (filtercomps && *filtercomps) free(filtercomps); + + if (rv != LDAPU_SUCCESS) return rv; + + /* look for "CmapLdapAttr" property and store it into searchAttr */ + rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_CERTMAP_LDAP_ATTR, + &searchAttr); + + if (rv == LDAPU_FAILED || !searchAttr || !*searchAttr) + rv = LDAPU_SUCCESS; + else { + certinfo->searchAttr = searchAttr ? strdup(searchAttr) : 0; + + if (searchAttr && !certinfo->searchAttr) + rv = LDAPU_ERR_OUT_OF_MEMORY; + else + rv = LDAPU_SUCCESS; + } + + if (rv != LDAPU_SUCCESS) return rv; + + /* look for verifycert property and set the default verify function */ + /* The value of the verifycert property is ignored */ + rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_VERIFYCERT, &verify); + + if (rv == LDAPU_SUCCESS) { + if (!ldapu_strcasecmp(verify, "on")) + certinfo->verifyCert = 1; + else if (!ldapu_strcasecmp(verify, "off")) + certinfo->verifyCert = 0; + else if (!verify || !*verify) /* for mail/news backward compatibilty */ + certinfo->verifyCert = 1; /* otherwise, this should be an error */ + else + rv = LDAPU_ERR_MISSING_VERIFYCERT_VAL; + } + else if (rv == LDAPU_FAILED) rv = LDAPU_SUCCESS; + + if (verify && *verify) free(verify); + + if (rv != LDAPU_SUCCESS) return rv; + + { + PRLibrary *lib = 0; + + /* look for the library property and load it */ + rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_LIBRARY, &libname); + + if (rv == LDAPU_SUCCESS) { + if (libname && *libname) { + lib = PR_LoadLibrary(libname); + if (!lib) rv = LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN; + } + else { + rv = LDAPU_ERR_MISSING_LIBNAME; + } + } + else if (rv == LDAPU_FAILED) rv = LDAPU_SUCCESS; + + if (libname) free(libname); + if (rv != LDAPU_SUCCESS) return rv; + + /* look for the InitFn property, find it in the libray and call it */ + rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_INITFN, &fname); + + if (rv == LDAPU_SUCCESS) { + if (fname && *fname) { + /* If lib is NULL, PR_FindSymbol will search all libs loaded + * through PR_LoadLibrary. + */ + CertMapInitFn_t fn = (CertMapInitFn_t)PR_FindSymbol(lib, fname); + + if (!fn) { + rv = LDAPU_ERR_MISSING_INIT_FN_IN_LIB; + } + else { + rv = (*fn)(certinfo, certinfo->issuerName, + certinfo->issuerDN, this_dllname); + } + } + else { + rv = LDAPU_ERR_MISSING_INIT_FN_NAME; + } + } + else if (lib) { + /* If library is specified, init function must be specified */ + /* If init fn is specified, library may not be specified */ + rv = LDAPU_ERR_MISSING_INIT_FN_IN_CONFIG; + } + else if (rv == LDAPU_FAILED) rv = LDAPU_SUCCESS; + + if (fname) free(fname); + + if (rv != LDAPU_SUCCESS) return rv; + } + + return rv; +} + +/* This function will read multiple certmap directives and set the information + * in the global certmap_listinfo structure. + */ +int certmap_read_certconfig_file (const char *file) +{ + DBConfInfo_t *conf_info = 0; + int rv; + + /* Read the config file */ + rv = dbconf_read_config_file_sub(file, LIB_DIRECTIVE, LIB_DIRECTIVE_LEN, + &conf_info); + + /* Convert the conf_info into certmap_listinfo. Some of the + * sub-structures are simply hijacked rather than copied since we are + * going to (carefully) free the conf_info anyway. + */ + + if (rv == LDAPU_SUCCESS && conf_info) { + DBConfDBInfo_t *nextdb; + DBConfDBInfo_t *curdb; + LDAPUCertMapInfo_t *certinfo; + + curdb = conf_info->firstdb; + + while (curdb) { + nextdb = curdb->next; + rv = dbinfo_to_certinfo(curdb, &certinfo); + + if (rv != LDAPU_SUCCESS) { + dbconf_free_confinfo(conf_info); + return rv; + } + + rv = process_certinfo(certinfo); + + if (rv != LDAPU_SUCCESS) { + dbconf_free_confinfo(conf_info); + return rv; + } + + curdb = nextdb; + } + + dbconf_free_confinfo(conf_info); + } + else { + rv = dbconf_to_certmap_err(rv); + } + + return rv; +} + +/* This function will read the "certmap default" directive from the config + * file and set the information in the global certmap_info. + */ +int certmap_read_default_certinfo (const char *file) +{ + DBConfDBInfo_t *db_info = 0; + int rv; + + rv = dbconf_read_default_dbinfo_sub(file, LIB_DIRECTIVE, LIB_DIRECTIVE_LEN, + &db_info); + + if (rv != LDAPU_SUCCESS) return rv; + + rv = dbinfo_to_certinfo(db_info, &default_certmap_info); + + dbconf_free_dbinfo(db_info); + return rv; +} + +static int ldapu_cert_searchfn_default (void *cert, LDAP *ld, + void *certmap_info_in, + const char *basedn, const char *dn, + const char *filter, + const char **attrs, LDAPMessage ***res) +{ + int rv = LDAPU_FAILED; + const char *ldapdn; + LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)certmap_info_in; + LDAPMessage * single_res = NULL; + LDAPMessage ** multiple_res = NULL; + + + if (certmap_info && certmap_info->searchAttr) { + char *subjectDN = 0; + char *certFilter = 0; + int len; + + rv = ldapu_get_cert_subject_dn(cert, &subjectDN); + + if (rv != LDAPU_SUCCESS || !subjectDN || !*subjectDN) return rv; + len = strlen(certmap_info->searchAttr) + strlen(subjectDN) + + strlen("=") + 1; + certFilter = (char *)malloc(len * sizeof(char)); + if (!certFilter) return LDAPU_ERR_OUT_OF_MEMORY; + sprintf(certFilter, "%s=%s", certmap_info->searchAttr, subjectDN); + free(subjectDN); + if ( ldapu_strcasecmp(basedn, "") ) { + rv = ldapu_find(ld, basedn, LDAP_SCOPE_SUBTREE, certFilter, attrs, 0, &single_res); + ldapu_free((void *)certFilter); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = (LDAPMessage **) ldapu_malloc(2 * sizeof(LDAPMessage *)); + (*res)[0] = single_res; + (*res)[1] = NULL; + return rv; + } else if (single_res) { + ldapu_msgfree(ld, single_res); + single_res = 0; + } + } else { + rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, certFilter, attrs, 0, &multiple_res); + ldapu_free((void *)certFilter); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = multiple_res; + return rv; + } else if (multiple_res) { + int n; + for ( n = 0; multiple_res[n] != NULL; n++) + ldapu_msgfree(ld, multiple_res[n]); + ldapu_memfree(ld, multiple_res); + } + } + + } + + if (dn && *dn) { + /* First do the base level search --- NOT ANY MORE!! */ + /* We actually do the search on the whole subtree hanging from "ldapdn" since we want to + * find all the entries that match the filter. + * If we get more than one matching entry in return, it'll be at verify time that we'll + * choose the correct one among them all. + * However, if certificate verify is not active, certificate mapping will fail and will + * consequently display an error message (something done at 'handle_handshake_done' level, + * for instance). */ + ldapdn = dn; + + if ( ldapu_strcasecmp(ldapdn, "") ) { + rv = ldapu_find(ld, ldapdn, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &single_res); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = (LDAPMessage **) ldapu_malloc(2 * sizeof(LDAPMessage *)); + (*res)[0] = single_res; + (*res)[1] = NULL; + return rv; + } else if (single_res) { + ldapu_msgfree(ld, single_res); + single_res = 0; + } + } else { + rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &multiple_res); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = multiple_res; + return rv; + } else if (multiple_res) { + int n; + for ( n = 0; multiple_res[n] != NULL; n++) + ldapu_msgfree(ld, multiple_res[n]); + ldapu_memfree(ld, multiple_res); + } + } + } else { + /* default the dn and filter for subtree search */ + ldapdn = basedn; + if (!filter || !*filter) { + if (certmap_info && certmap_info->searchAttr) { + /* dn & filter returned by the mapping function are both NULL + and 'searchAttr' based search has failed. Don't do brute + force search if 'searchAttr' is being used. Otherwise, + this search will result in all LDAP entries being + returned. + */ + } + else { + filter = "objectclass=*"; + } + } + } + + /* For local LDAP DB, the LDAP_SCOPE_BASE search may fail for dn == basedn + * since that object doesn't actually exists. + */ + if ((rv == LDAPU_FAILED || rv == LDAP_NO_SUCH_OBJECT) && filter && (!dn || !*dn)) { + + /* Try the subtree search only if the filter is non-NULL */ + if ( ldapu_strcasecmp(ldapdn, "") ) { + rv = ldapu_find(ld, ldapdn, LDAP_SCOPE_SUBTREE, filter, 0, 0, &single_res); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = (LDAPMessage **) ldapu_malloc(2 * sizeof(LDAPMessage *)); + (*res)[0] = single_res; + (*res)[1] = NULL; + return rv; + } else if (single_res) { + ldapu_msgfree(ld, single_res); + single_res = 0; + } + } else { + rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, filter, 0, 0, &multiple_res); + if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { + *res = multiple_res; + return rv; + } else if (multiple_res) { + int n; + for ( n = 0; multiple_res[n] != NULL; n++) + ldapu_msgfree(ld, multiple_res[n]); + ldapu_memfree(ld, multiple_res); + } + } + } + + if (rv == LDAPU_FAILED) { + /* Not an error but couldn't map the cert */ + rv = LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND; + } + else if ((!dn || !*dn) && (rv == LDAP_NO_SUCH_OBJECT)) { + rv = LDAPU_ERR_INVALID_SUFFIX; + } + + return rv; +} + +NSAPI_PUBLIC int ldapu_issuer_certinfo (const char *issuerDN, void **certmap_info) +{ + *certmap_info = 0; + + if (!issuerDN || !*issuerDN || !ldapu_strcasecmp(issuerDN, "default")) { + *certmap_info = default_certmap_info; + } + else if (certmap_listinfo) { + char *n_issuerDN = ldapu_dn_normalize (ldapu_strdup(issuerDN)); + LDAPUListNode_t *cur = certmap_listinfo->head; + int rv = LDAPU_FAILED; + while(cur) { + if (!ldapu_strcasecmp(n_issuerDN, ((LDAPUCertMapInfo_t *)cur->info)->issuerDN)) + { + *certmap_info = cur->info; + rv = LDAPU_SUCCESS; + break; + } + cur = cur->next; + } + if (n_issuerDN) ldapu_free (n_issuerDN); + } + return *certmap_info ? LDAPU_SUCCESS : LDAPU_FAILED; +} + +NSAPI_PUBLIC int ldapu_certmap_info_attrval (void *certmap_info_in, + const char *attr, char **val) +{ + /* Look for given attr in the certmap_info and return its value */ + LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)certmap_info_in; + LDAPUListNode_t *curprop = certmap_info->propval ? certmap_info->propval->head : 0; + LDAPUPropVal_t *propval; + int rv = LDAPU_FAILED; + + *val = 0; + while(curprop) { + propval = (LDAPUPropVal_t *)curprop->info; + if (!ldapu_strcasecmp(propval->prop, attr)) { + *val = propval->val ? strdup(propval->val) : 0; + rv = LDAPU_SUCCESS; + break; + } + curprop = curprop->next; + } + + return rv; +} + +static int AddAVAToBuf (char *buf, int size, int *len, + const char *tagName, CERTAVA *ava) +{ + int lenLen; + int taglen; + SECStatus rv; + + buf += *len; + + /* Copied from ns/lib/libsec ... + * XXX this code is incorrect in general + * -- should use a DER template. + */ + lenLen = 2; + if (ava->value.len >= 128) lenLen = 3; + + taglen = PL_strlen(tagName); + memcpy(buf, tagName, taglen); + buf[taglen++] = '='; + + rv = CERT_RFC1485_EscapeAndQuote(buf+taglen, + size-taglen, + (char*) ava->value.data + lenLen, + ava->value.len - lenLen); + + *len += strlen(buf); + + return (rv == SECSuccess ? LDAPU_SUCCESS : LDAPU_FAILED); +} + +static int AddToLdapDN (char *ldapdn, int size, int *dnlen, + const char *tagName, CERTAVA *ava) +{ + char *dn = ldapdn + *dnlen; + + if (*dnlen) { strcat(dn, ", "); dn += 2; *dnlen += 2; } + return AddAVAToBuf(ldapdn, size, dnlen, tagName, ava); +} + +static int AddToFilter (char *filter, int size, int *flen, + const char *tagName, CERTAVA *ava) +{ + int rv; + + /* Append opening parenthesis */ + strcat(filter + *flen, " ("); + *flen += 2; + rv = AddAVAToBuf(filter, size, flen, tagName, ava); + + if (rv != LDAPU_SUCCESS) return rv; + + /* Append closing parenthesis */ + strcat(filter + *flen, ")"); + (*flen)++; + + return rv; +} + +NSAPI_PUBLIC int ldapu_free_cert_ava_val (char **val) +{ + char **ptr = val; + + if (!val) return LDAPU_SUCCESS; + + while(*ptr) free(*ptr++); + free(val); + + return LDAPU_SUCCESS; +} + +static int ldapu_cert_mapfn_default (void *cert_in, LDAP *ld, + void *certmap_info_in, + char **ldapDN_out, char **filter_out) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + LDAPUCertMapInfo_t *certmap_info= (LDAPUCertMapInfo_t *)certmap_info_in; + int rv = LDAPU_SUCCESS; + + *ldapDN_out = *filter_out = 0; + + if (!certmap_info) { + /* Use subject DN as is -- identity mapping function */ + rv = ldapu_get_cert_subject_dn(cert, ldapDN_out); + + return rv; + } + else { + /* + * Iterate over rdns from the subject and collect AVAs depending on + * dnComps and filtercomps to form ldapDN and filter respectively. + * certmap_info->dncomps + */ + CERTName *subject = &cert->subject; + CERTRDN **rdns = subject->rdns; + CERTRDN **lastRdn; + CERTRDN **rdn; + CERTAVA **avas; + CERTAVA *ava; + char ldapdn[BIG_LINE]; + char filter[BIG_LINE]; + int dnlen = 0; /* ldap DN length */ + int flen = 0; /* filter length */ + int numfavas = 0; /* no of avas added to filter */ + + if (rdns == NULL) { + /* error */ + } + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) lastRdn++; + lastRdn--; + + /* Initialize filter to "(&" */ + strcpy(filter, "(&"); + flen = 2; + + /* + * Loop over subject rdns in the _reverse_ order while forming ldapDN + * and filter. + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + avas = (*rdn)->avas; + while ((ava = *avas++) != NULL) { + int tag = CERT_GetAVATag(ava); + const char *tagName = certmap_secoid_to_name(tag); + + if (PresentInComps(certmap_info->dncomps, tag)) { + rv = AddToLdapDN(ldapdn, BIG_LINE, &dnlen, tagName, ava); + if (rv != LDAPU_SUCCESS) return rv; + } + + if (PresentInComps(certmap_info->filtercomps, tag)) { + rv = AddToFilter(filter, BIG_LINE, &flen, tagName, ava); + if (rv != LDAPU_SUCCESS) return rv; + numfavas++; + } + } + } + + if (numfavas == 0) { + /* nothing added to filter */ + *filter = 0; + } + else if (numfavas == 1) { + /* one ava added to filter -- remove "(& (" from the front and ")" + * from the end. + */ + *filter_out = strdup(filter+4); + if (!*filter_out) return LDAPU_ERR_OUT_OF_MEMORY; + (*filter_out)[strlen(*filter_out)-1] = 0; + } + else { + /* Add the closing parenthesis to filter */ + strcat(filter+flen, ")"); + *filter_out = strdup(filter); + } + + if (dnlen >= BIG_LINE) return LDAPU_FAILED; + ldapdn[dnlen] = 0; + *ldapDN_out = *ldapdn ? strdup(ldapdn) : 0; + + if ((numfavas && !*filter_out) || (dnlen && !*ldapDN_out)) { + /* strdup failed */ + return LDAPU_ERR_OUT_OF_MEMORY; + } + + if ((certmap_info->dncompsState == COMPS_HAS_ATTRS && dnlen == 0) || + (certmap_info->filtercompsState == COMPS_HAS_ATTRS && + numfavas == 0)) + { + /* At least one attr in DNComps should be present in the cert */ + /* Same is true for FilterComps */ + rv = LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND; + } + } + + return rv; +} + +NSAPI_PUBLIC int ldapu_set_cert_mapfn (const char *issuerDN, + CertMapFn_t mapfn) +{ + LDAPUCertMapInfo_t *certmap_info; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + + /* Don't set the mapping function if certmap_info doesen't exist */ + if (rv != LDAPU_SUCCESS) return rv; + + certmap_info->mapfn = mapfn; + return LDAPU_SUCCESS; +} + +static CertMapFn_t ldapu_get_cert_mapfn_sub (LDAPUCertMapInfo_t *certmap_info) +{ + CertMapFn_t mapfn; + + if (certmap_info && certmap_info->mapfn) + mapfn = certmap_info->mapfn; + else if (default_certmap_info && default_certmap_info->mapfn) + mapfn = default_certmap_info->mapfn; + else + mapfn = ldapu_cert_mapfn_default; + + return mapfn; +} + +NSAPI_PUBLIC CertMapFn_t ldapu_get_cert_mapfn (const char *issuerDN) +{ + LDAPUCertMapInfo_t *certmap_info = 0; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + /* certmap_info may be NULL -- use the default */ + + return ldapu_get_cert_mapfn_sub(certmap_info); +} + +NSAPI_PUBLIC int ldapu_set_cert_searchfn (const char *issuerDN, + CertSearchFn_t searchfn) +{ + LDAPUCertMapInfo_t *certmap_info; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + + /* Don't set the mapping function if certmap_info doesen't exist */ + if (rv != LDAPU_SUCCESS) return rv; + + certmap_info->searchfn = searchfn; + return LDAPU_SUCCESS; +} + +static CertSearchFn_t ldapu_get_cert_searchfn_sub (LDAPUCertMapInfo_t *certmap_info) +{ + CertSearchFn_t searchfn; + + if (certmap_info && certmap_info->searchfn) + searchfn = certmap_info->searchfn; + else if (default_certmap_info && default_certmap_info->searchfn) + searchfn = default_certmap_info->searchfn; + else + searchfn = ldapu_cert_searchfn_default; + + return searchfn; +} + +NSAPI_PUBLIC CertSearchFn_t ldapu_get_cert_searchfn (const char *issuerDN) +{ + LDAPUCertMapInfo_t *certmap_info = 0; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + /* certmap_info may be NULL -- use the default */ + + return ldapu_get_cert_searchfn_sub(certmap_info); +} + +NSAPI_PUBLIC int ldapu_set_cert_verifyfn (const char *issuerDN, + CertVerifyFn_t verifyfn) +{ + LDAPUCertMapInfo_t *certmap_info; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + + /* Don't set the verify function if certmap_info doesen't exist */ + if (rv != LDAPU_SUCCESS) return rv; + + certmap_info->verifyfn = verifyfn; + return LDAPU_SUCCESS; +} + +static CertVerifyFn_t ldapu_get_cert_verifyfn_sub (LDAPUCertMapInfo_t *certmap_info) +{ + CertVerifyFn_t verifyfn; + + if (certmap_info && certmap_info->verifyfn) + verifyfn = certmap_info->verifyfn; + else if (default_certmap_info && default_certmap_info->verifyfn) + verifyfn = default_certmap_info->verifyfn; + else + verifyfn = ldapu_cert_verifyfn_default; + + return verifyfn; +} + +NSAPI_PUBLIC CertVerifyFn_t ldapu_get_cert_verifyfn (const char *issuerDN) +{ + LDAPUCertMapInfo_t *certmap_info = 0; + int rv; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + /* certmap_info may be NULL -- use the default */ + + return ldapu_get_cert_verifyfn_sub(certmap_info); +} + +static int ldapu_certinfo_copy (const LDAPUCertMapInfo_t *from, + const char *newIssuerName, + const char *newIssuerDN, + LDAPUCertMapInfo_t *to) +{ + /* This function is not tested and is not used */ + int rv; + + to->issuerName = newIssuerName ? strdup(newIssuerName) : 0; + to->issuerDN = newIssuerDN ? strdup(newIssuerDN) : 0; + if (from->propval) { + rv = ldapu_list_copy(from->propval, &to->propval, ldapu_propval_copy); + if (rv != LDAPU_SUCCESS) return rv; + } + else { + to->propval = 0; + } + + return process_certinfo(to); +} + +NSAPI_PUBLIC int ldapu_cert_to_ldap_entry (void *cert, LDAP *ld, + const char *basedn, + LDAPMessage **res) +{ + char *issuerDN = 0; + char *ldapDN = 0; + char *filter = 0; + LDAPUCertMapInfo_t *certmap_info; + LDAPMessage **res_array = NULL; + CertMapFn_t mapfn; + CertVerifyFn_t verifyfn; + CertSearchFn_t searchfn; + void *entry_cert = 0; + int rv,i,j; + + *res = 0; + + if (!certmap_attrs[0]) { + /* Initialize certmap_attrs */ + certmap_attrs[0] = ldapu_strings[LDAPU_STR_ATTR_USER]; + certmap_attrs[1] = ldapu_strings[LDAPU_STR_ATTR_CERT]; + certmap_attrs[2] = ldapu_strings[LDAPU_STR_ATTR_CERT_NOSUBTYPE]; + certmap_attrs[3] = 0; + } + + rv = ldapu_get_cert_issuer_dn(cert, &issuerDN); + + if (rv != LDAPU_SUCCESS) return LDAPU_ERR_NO_ISSUERDN_IN_CERT; + + /* don't free the certmap_info -- its a pointer to an internal structure */ + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); + free(issuerDN); + + if (!certmap_info) certmap_info = default_certmap_info; + + /* Get the mapping function from the certmap_info */ + mapfn = ldapu_get_cert_mapfn_sub(certmap_info); + + rv = (*mapfn)(cert, ld, certmap_info, &ldapDN, &filter); + + if (rv != LDAPU_SUCCESS) return rv; + + /* Get the search function from the certmap_info - certinfo maybe NULL */ + searchfn = ldapu_get_cert_searchfn_sub(certmap_info); + + rv = (*searchfn)(cert, ld, certmap_info, basedn, ldapDN, filter, + certmap_attrs, &res_array); + + if (ldapDN) free(ldapDN); + if (filter) free(filter); + + /* + * Get the verify cert function & call it. + */ + j = 0; + if ((rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) && + (certmap_info ? certmap_info->verifyCert : 0)) + { + verifyfn = ldapu_get_cert_verifyfn_sub(certmap_info); + + if (verifyfn) { + int verify_rv; + + i = 0; + do { + LDAPMessage *entry; + verify_rv = (*verifyfn)(cert, ld, certmap_info, res_array[i], &entry); + + if (rv == LDAPU_ERR_MULTIPLE_MATCHES) { + if (verify_rv == LDAPU_SUCCESS) { + /* 'entry' points to the matched entry */ + /* Get the 'res' which only contains this entry */ + char *dn = ldapu_get_dn(ld, entry); + if (*res) ldapu_msgfree(ld, *res); + rv = ldapu_find(ld, dn, LDAP_SCOPE_BASE, 0, certmap_attrs, 0, res); + ldapu_memfree(ld, dn); + } + else { + /* Verify failed for multiple matches -- keep rv */ + /* multiple matches err is probably more interesting to + the caller then any other error returned by the verify + fn */ + } + } + else /* rv == LDAPU_SUCCESS */ { + if (verify_rv == LDAPU_SUCCESS) { + *res = res_array[0]; + j = 1; + } else { + rv = verify_rv; + } + } + } while ( (verify_rv != LDAPU_SUCCESS) && (res_array[++i] != NULL) ); + } + } else { + if (rv == LDAPU_SUCCESS) { + *res = res_array[0]; + j = 1; + } + } + + + + if (rv != LDAPU_SUCCESS) { + if (*res) { ldapu_msgfree(ld, *res); *res = 0; } + } + + i = j; /* ugaston - if the search had been successful, despite verifyCert being "off", + * mapping is considered successful, so we keep the first (and only) response message. + * If, on the other hand, the search had returned multiple matches, the fact + * of having verifyCert "off" automatically turns the mapping faulty, so we + * don't need to care about keeping any response at all. + */ + + if (res_array) { + while (res_array[i] != NULL) { + ldapu_msgfree(ld, res_array[i]); + res_array[i++] = 0; + } + ldapu_memfree(ld, res_array); + } + return rv; +} + +/* The caller shouldn't free the entry */ +NSAPI_PUBLIC int ldapu_cert_to_user (void *cert, LDAP *ld, const char *basedn, + LDAPMessage **res_out, char **user) +{ + int rv; + LDAPMessage *res; + LDAPMessage *entry; + int numEntries; + char **attrVals; + + *res_out = 0; + *user = 0; + + rv = ldapu_cert_to_ldap_entry(cert, ld, basedn, &res); + + if (rv != LDAPU_SUCCESS) { + return rv; + } + + if (!res) { + return LDAPU_ERR_EMPTY_LDAP_RESULT; + } + + /* Extract user login (the 'uid' attr) from 'res' */ + numEntries = ldapu_count_entries(ld, res); + + if (numEntries != 1) { + return LDAPU_ERR_MULTIPLE_MATCHES; + } + + entry = ldapu_first_entry(ld, res); + + if (!entry) { + return LDAPU_ERR_MISSING_RES_ENTRY; + } + + attrVals = ldapu_get_values(ld, entry, + ldapu_strings[LDAPU_STR_ATTR_USER]); + + if (!attrVals || !attrVals[0]) { + return LDAPU_ERR_MISSING_UID_ATTR; + } + + *user = strdup(attrVals[0]); + ldapu_value_free(ld, attrVals); + +/* ldapu_msgfree(res); */ + + if (!*user) { + return LDAPU_ERR_OUT_OF_MEMORY; + } + + *res_out = res; + return LDAPU_SUCCESS; +} + + +NSAPI_PUBLIC int ldapu_certinfo_modify (const char *issuerName, + const char *issuerDN, + const LDAPUPropValList_t *propval) +{ + LDAPUCertMapInfo_t *certinfo = 0; + int rv; + + /* Make sure issuerName & issuerDN are both NULL or are both non-NULL */ + if (!issuerName || !*issuerName) { + /* issuerDN must be NULL */ + if (issuerDN) return LDAPU_ERR_WRONG_ARGS; + } + else if (!issuerDN || !*issuerDN) { + /* error - issuerName must be NULL but it is not */ + return LDAPU_ERR_WRONG_ARGS; + } + + if (!issuerDN) { + /* Modify the default certinfo */ + certinfo = default_certmap_info; + } + else { + rv = ldapu_issuer_certinfo(issuerDN, (void **)&certinfo); + + if (rv != LDAPU_SUCCESS) { + /* allocate new certinfo & add to the list */ + certinfo = (LDAPUCertMapInfo_t *)malloc(sizeof(LDAPUCertMapInfo_t)); + if (!certinfo) return LDAPU_ERR_OUT_OF_MEMORY; + memset((void *)certinfo, 0, sizeof(LDAPUCertMapInfo_t)); + + certinfo->issuerName = strdup(issuerName); + certinfo->issuerDN = strdup(issuerDN); + + if (!certinfo->issuerName || !certinfo->issuerDN) + return LDAPU_ERR_OUT_OF_MEMORY; + } + } + + /* Now modify the certinfo */ + /* Free the old propval list and add new propval */ + ldapu_propval_list_free(certinfo->propval); + + if (propval) { + rv = ldapu_list_copy (propval, &certinfo->propval, ldapu_propval_copy); + if (rv != LDAPU_SUCCESS) return rv; + } + + /* process_certinfo processes the info and adds to the certmap_listinfo */ + process_certinfo(certinfo); + + return LDAPU_SUCCESS; +} + +/* ldapu_propval_same - returns LDAPU_SUCCESS or LDAPU_FAILED */ +static void * ldapu_propval_same (void *info, void *find_arg) +{ + /* check if info has find_arg as the issuerDN */ + const char *issuerDN = (const char *)find_arg; + const LDAPUCertMapInfo_t *certinfo = (const LDAPUCertMapInfo_t *) info; + + if (!ldapu_strcasecmp(certinfo->issuerDN, issuerDN)) + return (void *)LDAPU_SUCCESS; + else + return (void *)LDAPU_FAILED; +} + +NSAPI_PUBLIC int ldapu_certinfo_delete (const char *issuerDN) +{ + int rv; + LDAPUListNode_t *node; + + if (!issuerDN || !*issuerDN) + return LDAPU_ERR_WRONG_ARGS; + + rv = ldapu_list_find_node (certmap_listinfo, &node, ldapu_propval_same, + (void *)issuerDN); + + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_list_remove_node (certmap_listinfo, node); + + return rv; +} + +NSAPI_PUBLIC int ldapu_certinfo_save (const char *fname, + const char *old_fname, + const char *tmp_fname) +{ + /* Copy the header from the old_fname into a temporary file + * Save the default_certmap_info and certmap_listinfo into the temporary + * file. Rename the temporary file to the new file. + */ + FILE *ofp; + FILE *tfp; + char buf[BIG_LINE]; + char *ptr; + int eof; + int rv; + LDAPUPrintInfo_t pinfo; + +#ifdef XP_WIN32 + if ((ofp = fopen(old_fname, "rt")) == NULL) +#else + if ((ofp = fopen(old_fname, "r")) == NULL) +#endif + { + return LDAPU_ERR_CANNOT_OPEN_FILE; + } + + if ((tfp = fopen(tmp_fname, "w")) == NULL) + { + return LDAPU_ERR_CANNOT_OPEN_FILE; + } + + eof = 0; + while(!eof) { + if (!fgets(buf, BIG_LINE, ofp)) break; + + ptr = buf; + + /* skip leading whitespace */ + while(*ptr && isspace(*ptr)) ++ptr; + + if (*ptr && *ptr != '#') { + /* It's not a comment, we are done */ + break; + } + + fprintf(tfp, "%s", buf); + } + + fclose(ofp); + + /* Output the default_certmap_info */ + pinfo.fp = tfp; + pinfo.arg = default_certmap_info->issuerName; + + rv = (int)ldapu_certinfo_print (default_certmap_info, &pinfo); + + if (rv != LDAPU_SUCCESS) { + fclose(tfp); + return rv; + } + + if (certmap_listinfo) { + rv = ldapu_list_print (certmap_listinfo, ldapu_certinfo_print, + &pinfo); + + if (rv != LDAPU_SUCCESS) { + fclose(tfp); + return rv; + } + } + + fclose(tfp); + + /* replace old file with the tmp file */ +#ifdef _WIN32 + if ( !MoveFileEx(tmp_fname, fname, MOVEFILE_REPLACE_EXISTING )) +#else + if ( rename( tmp_fname, fname) != 0 ) +#endif + { + return LDAPU_ERR_RENAME_FILE_FAILED; + } + + return LDAPU_SUCCESS; +} + +static void * ldapu_propval_free (void *propval_in, void *arg) +{ + LDAPUPropVal_t *propval = (LDAPUPropVal_t *)propval_in; + + if (propval->prop) free(propval->prop); + if (propval->val) free(propval->val); + memset((void *)propval, 0, sizeof(LDAPUPropVal_t)); + free(propval); + return 0; +} + +void ldapu_certinfo_free (void *info_in) +{ + LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)info_in; + + if (certmap_info->issuerName) free(certmap_info->issuerName); + if (certmap_info->issuerDN) free(certmap_info->issuerDN); + if (certmap_info->propval) + ldapu_list_free(certmap_info->propval, ldapu_propval_free); + if (certmap_info->searchAttr) free(certmap_info->searchAttr); + memset((void *)certmap_info, 0, sizeof(LDAPUCertMapInfo_t)); + free(certmap_info); +} + +static void * ldapu_certinfo_free_helper (void *info, void *arg) +{ + ldapu_certinfo_free(info); + return (void *)LDAPU_SUCCESS; +} + +void ldapu_certmap_listinfo_free (void *certmap_listinfo) +{ + LDAPUCertMapListInfo_t *list = (LDAPUCertMapListInfo_t *)certmap_listinfo; + ldapu_list_free(list, ldapu_certinfo_free_helper); +} + +void ldapu_propval_list_free (void *propval_list) +{ + LDAPUPropValList_t *list = (LDAPUPropValList_t *)propval_list; + ldapu_list_free(list, ldapu_propval_free); +} + +int ldapu_certmap_init (const char *config_file, + const char *dllname, + LDAPUCertMapListInfo_t **certmap_list, + LDAPUCertMapInfo_t **certmap_default) +{ + int rv; + certmap_listinfo = (LDAPUCertMapListInfo_t *)malloc(sizeof(LDAPUCertMapListInfo_t)); + + *certmap_list = 0; + *certmap_default = 0; + sprintf(this_dllname, "%s", dllname); + + if (!certmap_listinfo) return LDAPU_ERR_OUT_OF_MEMORY; + + memset((void *)certmap_listinfo, 0, sizeof(LDAPUCertMapListInfo_t)); + + rv = certmap_read_certconfig_file(config_file); + + if (rv == LDAPU_SUCCESS) { + *certmap_list = certmap_listinfo; + *certmap_default = default_certmap_info; + } + + return rv; +} + +NSAPI_PUBLIC int ldaputil_exit () +{ + if (default_certmap_info) { + ldapu_certinfo_free(default_certmap_info); + default_certmap_info = 0; + } + + if (certmap_listinfo) { + ldapu_certmap_listinfo_free(certmap_listinfo); + certmap_listinfo = 0; + } + + return LDAPU_SUCCESS; +} + + +NSAPI_PUBLIC void ldapu_free (void *ptr) +{ + if (ptr) free(ptr); +} + +NSAPI_PUBLIC void ldapu_free_old (char *ptr) +{ + free((void *)ptr); +} + +NSAPI_PUBLIC void *ldapu_malloc (int size) +{ + return malloc(size); +} + +NSAPI_PUBLIC char *ldapu_strdup (const char *ptr) +{ + return strdup(ptr); +} + +NSAPI_PUBLIC void *ldapu_realloc (void *ptr, int size) +{ + return realloc(ptr, size); +} + +#define DNSEPARATOR(c) (c == ',' || c == ';') +#define SEPARATOR(c) (c == ',' || c == ';' || c == '+') +#define SPACE(c) (c == ' ' || c == '\n') +#define NEEDSESCAPE(c) (c == '\\' || c == '"') +#define B4TYPE 0 +#define INTYPE 1 +#define B4EQUAL 2 +#define B4VALUE 3 +#define INVALUE 4 +#define INQUOTEDVALUE 5 +#define B4SEPARATOR 6 + +static char * +ldapu_dn_normalize( char *dn ) +{ + char *d, *s; + int state, gotesc; + + gotesc = 0; + state = B4TYPE; + for ( d = s = dn; *s; s++ ) { + switch ( state ) { + case B4TYPE: + if ( ! SPACE( *s ) ) { + state = INTYPE; + *d++ = *s; + } + break; + case INTYPE: + if ( *s == '=' ) { + state = B4VALUE; + *d++ = *s; + } else if ( SPACE( *s ) ) { + state = B4EQUAL; + } else { + *d++ = *s; + } + break; + case B4EQUAL: + if ( *s == '=' ) { + state = B4VALUE; + *d++ = *s; + } else if ( ! SPACE( *s ) ) { + /* not a valid dn - but what can we do here? */ + *d++ = *s; + } + break; + case B4VALUE: + if ( *s == '"' ) { + state = INQUOTEDVALUE; + *d++ = *s; + } else if ( ! SPACE( *s ) ) { + state = INVALUE; + *d++ = *s; + } + break; + case INVALUE: + if ( !gotesc && SEPARATOR( *s ) ) { + while ( SPACE( *(d - 1) ) ) + d--; + state = B4TYPE; + if ( *s == '+' ) { + *d++ = *s; + } else { + *d++ = ','; + } + } else if ( gotesc && !NEEDSESCAPE( *s ) && + !SEPARATOR( *s ) ) { + *--d = *s; + d++; + } else { + *d++ = *s; + } + break; + case INQUOTEDVALUE: + if ( !gotesc && *s == '"' ) { + state = B4SEPARATOR; + *d++ = *s; + } else if ( gotesc && !NEEDSESCAPE( *s ) ) { + *--d = *s; + d++; + } else { + *d++ = *s; + } + break; + case B4SEPARATOR: + if ( SEPARATOR( *s ) ) { + state = B4TYPE; + if ( *s == '+' ) { + *d++ = *s; + } else { + *d++ = ','; + } + } + break; + default: + break; + } + if ( *s == '\\' ) { + gotesc = 1; + } else { + gotesc = 0; + } + } + *d = '\0'; + + /* Trim trailing spaces */ + d--; + while ( d >= dn && *d == ' ' ) { + *d-- = '\0'; + } + + return( dn ); +} diff --git a/lib/ldaputil/certmap.conf b/lib/ldaputil/certmap.conf new file mode 100644 index 00000000..35c1e9c8 --- /dev/null +++ b/lib/ldaputil/certmap.conf @@ -0,0 +1,48 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# This file configures how a certificate is mapped to an LDAP entry. See the +# documentation for more information on this file. +# +# The format of this file is as follows: +# certmap <name> <issuerDN> +# <name>:<prop1> [<val1>] +# <name>:<prop2> [<val2>] +# +# Notes: +# +# 1. Mapping can be defined per issuer of a certificate. If mapping doesn't +# exists for a particular 'issuerDN' then the server uses the default +# mapping. +# +# 2. There must be an entry for <name>=default and issuerDN "default". +# This mapping is the default mapping. +# +# 3. '#' can be used to comment out a line. +# +# 4. DNComps & FilterComps are used to form the base DN and filter resp. for +# performing an LDAP search while mapping the cert to a user entry. +# +# 5. DNComps can be one of the following: +# commented out - take the user's DN from the cert as is +# empty - search the entire LDAP tree (DN == suffix) +# attr names - a comma separated list of attributes to form DN +# +# 6. FilterComps can be one of the following: +# commented out - set the filter to "objectclass=*" +# empty - set the filter to "objectclass=*" +# attr names - a comma separated list of attributes to form the filter +# + +certmap default default +#default:DNComps +#default:FilterComps e, uid +#default:verifycert on +#default:CmapLdapAttr certSubjectDN +#default:library <path_to_shared_lib_or_dll> +#default:InitFn <Init function's name> diff --git a/lib/ldaputil/dbconf.c b/lib/ldaputil/dbconf.c new file mode 100644 index 00000000..684d62df --- /dev/null +++ b/lib/ldaputil/dbconf.c @@ -0,0 +1,704 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include <malloc.h> +#include <ctype.h> + +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> +#include <ldaputil/encode.h> +#include <ldaputil/dbconf.h> + +#define BIG_LINE 1024 + +static const char *DB_DIRECTIVE = "directory"; +static const int DB_DIRECTIVE_LEN = 9; /* strlen("DB_DIRECTIVE") */ + +static const char *ENCODED = "encoded"; + +static void insert_dbinfo_propval(DBConfDBInfo_t *db_info, + DBPropVal_t *propval) +{ + if (db_info->lastprop) { + db_info->lastprop->next = propval; + } + else { + db_info->firstprop = propval; + } + + db_info->lastprop = propval; +} + +static void insert_dbconf_dbinfo(DBConfInfo_t *conf_info, + DBConfDBInfo_t *db_info) +{ + if (conf_info->lastdb) { + conf_info->lastdb->next = db_info; + } + else { + conf_info->firstdb = db_info; + } + + conf_info->lastdb = db_info; +} + +void dbconf_free_propval (DBPropVal_t *propval) +{ + if (propval) { + if (propval->prop) free(propval->prop); + if (propval->val) free(propval->val); + memset((void *)propval, 0, sizeof(DBPropVal_t)); + free(propval); + } +} + +NSAPI_PUBLIC void dbconf_free_dbinfo (DBConfDBInfo_t *db_info) +{ + if (db_info) { + DBPropVal_t *next; + DBPropVal_t *cur; + + if (db_info->dbname) free(db_info->dbname); + if (db_info->url) free(db_info->url); + + cur = db_info->firstprop; + + while(cur) { + next = cur->next; + dbconf_free_propval(cur); + cur = next; + } + + memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); + free(db_info); + } +} + +NSAPI_PUBLIC void dbconf_free_confinfo (DBConfInfo_t *conf_info) +{ + DBConfDBInfo_t *next; + DBConfDBInfo_t *cur; + + if (conf_info) { + cur = conf_info->firstdb; + + while (cur) { + next = cur->next; + dbconf_free_dbinfo(cur); + cur = next; + } + + memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); + free(conf_info); + } +} + +static int skip_blank_lines_and_spaces(FILE *fp, char *buf, char **ptr_out, + int *eof) +{ + char *ptr = buf; + char *end; + + while(buf && (*buf || fgets(buf, BIG_LINE, fp))) { + ptr = buf; + + /* skip leading whitespace */ + while(*ptr && isspace(*ptr)) ++ptr; + + /* skip blank line or comment */ + if (!*ptr || *ptr == '#') { + *buf = 0; /* to force reading of next line */ + continue; + } + + /* Non-blank line found */ + break; + } + + *ptr_out = ptr; + if (!*ptr) { + *eof = 1; + } + else { + /* skip trailing whitespace */ + end = ptr + strlen(ptr) - 1; + while(isspace(*end)) *end-- = 0; + } + + return LDAPU_SUCCESS; +} + +static int dbconf_parse_propval (char *buf, char *ptr, + DBConfDBInfo_t *db_info) +{ + char *dbname = db_info->dbname; + int dbname_len = strlen(dbname); + char *prop; + char *val; + DBPropVal_t *propval; + char *delimeter_chars = " \t"; + char *lastchar; + int end_of_prop; + char *encval = 0; /* encoded value */ + char *origprop = 0; + + if ((ptr - buf + dbname_len > BIG_LINE) || + strncmp(ptr, dbname, dbname_len) || + !(ptr[dbname_len] == ':' || isspace(ptr[dbname_len]))) + { + /* Not a prop-val for the current db but not an error */ + return LDAPU_ERR_NOT_PROPVAL; + } + + /* remove the last char if it is newline */ + lastchar = strrchr(buf, '\n'); + if (lastchar) *lastchar = '\0'; + + prop = ptr + dbname_len + 1; + + while(*prop && (isspace(*prop) || *prop == ':')) ++prop; + + if (!*prop) { + return LDAPU_ERR_PROP_IS_MISSING; + } + + end_of_prop = strcspn(prop, delimeter_chars); + + if (prop[end_of_prop] != '\0') { + /* buf doesn't end here -- val is present */ + prop[end_of_prop] = '\0'; + val = &prop[end_of_prop + 1]; + + while(*val && isspace(*val)) ++val; + if (*val == '\0') val = 0; + } + else { + val = 0; + } + + /* + * The prop-val line could be one of the following: + * "<dbname>:prop val" OR "<dbname>:encoded prop encval" + * If (prop == "encoded") then the val has "prop encval". + * Get the actual prop from val and get encval (i.e. encoded value) + * and decode it. If it is encoded then the val part must be non-NULL. + */ + if (val && *val && !strcmp(prop, ENCODED)) { + /* val has the actual prop followed by the encoded value */ + origprop = prop; + prop = val; + while(*prop && (isspace(*prop) || *prop == ':')) ++prop; + + if (!*prop) { + return LDAPU_ERR_PROP_IS_MISSING; + } + + end_of_prop = strcspn(prop, delimeter_chars); + + if (prop[end_of_prop] != '\0') { + /* buf doesn't end here -- encval is present */ + prop[end_of_prop] = '\0'; + encval = &prop[end_of_prop + 1]; + + while(*encval && isspace(*encval)) ++encval; + if (*encval == '\0') encval = 0; + } + else { + encval = 0; + } + + if (!encval) { + /* special case - if encval is null, "encoded" itself is a + * property and what we have in prop now is the value. */ + val = prop; + prop = origprop; + } + else { + /* decode the value */ + val = dbconf_decodeval(encval); + } + } + + /* Success - we have prop & val */ + propval = (DBPropVal_t *)malloc(sizeof(DBPropVal_t)); + + if (!propval) return LDAPU_ERR_OUT_OF_MEMORY; + memset((void *)propval, 0, sizeof(DBPropVal_t)); + propval->prop = strdup(prop); + propval->val = val ? strdup(val) : 0; + + if (!propval->prop || (val && !propval->val)) { + dbconf_free_propval(propval); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + if (encval) free(val); /* val was allocated by dbconf_decodeval */ + + insert_dbinfo_propval(db_info, propval); + return LDAPU_SUCCESS; +} + +static int dbconf_read_propval (FILE *fp, char *buf, DBConfDBInfo_t *db_info, + int *eof) +{ + int rv; + char *ptr = buf; + + while(buf && (*buf || fgets(buf, BIG_LINE, fp))) { + ptr = buf; + + rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); + + if (rv != LDAPU_SUCCESS || *eof) return rv; + + /* We have a non-blank line which could be prop-val pair for the + * dbname in the db_info. parse the prop-val pair and continue. + */ + rv = dbconf_parse_propval(buf, ptr, db_info); + + if (rv == LDAPU_ERR_NOT_PROPVAL) return LDAPU_SUCCESS; + if (rv != LDAPU_SUCCESS) return rv; + + *buf = 0; /* to force reading of next line */ + } + + if (!*buf) *eof = 1; + + return LDAPU_SUCCESS; +} + +static int parse_directive(char *buf, const char *directive, + const int directive_len, + DBConfDBInfo_t **db_info_out) +{ + DBConfDBInfo_t *db_info; + char *dbname; + char *url; + int end_of_dbname; + char *delimeter_chars = " \t"; + char *lastchar; + + /* remove the last char if it is newline */ + lastchar = strrchr(buf, '\n'); + if (lastchar) *lastchar = '\0'; + + if (strncmp(buf, directive, directive_len) || + !isspace(buf[directive_len])) + { + return LDAPU_ERR_DIRECTIVE_IS_MISSING; + } + + dbname = buf + directive_len + 1; + + while(*dbname && isspace(*dbname)) ++dbname; + + if (!*dbname) { + return LDAPU_ERR_DBNAME_IS_MISSING; + } + + end_of_dbname = strcspn(dbname, delimeter_chars); + + if (dbname[end_of_dbname] != '\0') { + /* buf doesn't end here -- url is present */ + dbname[end_of_dbname] = '\0'; + url = &dbname[end_of_dbname + 1]; + + while(*url && isspace(*url)) ++url; + + if (*url == '\0') url = 0; + } + else { + url = 0; + } + + /* Success - we have dbname & url */ + db_info = (DBConfDBInfo_t *)malloc(sizeof(DBConfDBInfo_t)); + + if (!db_info) return LDAPU_ERR_OUT_OF_MEMORY; + memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); + db_info->dbname = strdup(dbname); + db_info->url = url ? strdup(url) : 0; + + if (!db_info->dbname || (url && !db_info->url)) { + dbconf_free_dbinfo(db_info); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + *db_info_out = db_info; + return LDAPU_SUCCESS; +} + +/* Read the next database info from the file and put it in db_info_out. The + * buf may contain first line of the database info. When this function + * finishes, the buf may contain unprocessed information (which should be + * passed to the next call to read_db_info). + */ +static int read_db_info (FILE *fp, char *buf, DBConfDBInfo_t **db_info_out, + const char *directive, const int directive_len, + int *eof) +{ + char *ptr; + int found_directive = 0; + DBConfDBInfo_t *db_info; + int rv; + + *db_info_out = 0; + + rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); + + if (rv != LDAPU_SUCCESS || *eof) return rv; + + /* We possibly have a directive of the form "directory <name> <url>" */ + rv = parse_directive(ptr, directive, directive_len, &db_info); + if (rv != LDAPU_SUCCESS) return rv; + + /* We have parsed the directive successfully -- lets look for additional + * property-value pairs for the database. + */ + if (!fgets(buf, BIG_LINE, fp)) { + *eof = 1; + rv = LDAPU_SUCCESS; + } + else { + rv = dbconf_read_propval(fp, buf, db_info, eof); + } + + if (rv != LDAPU_SUCCESS) { + dbconf_free_dbinfo(db_info); + *db_info_out = 0; + } + else { + *db_info_out = db_info; + } + + return rv; +} + +int dbconf_read_config_file_sub (const char *file, + const char *directive, + const int directive_len, + DBConfInfo_t **conf_info_out) +{ + FILE *fp; + DBConfInfo_t *conf_info; + DBConfDBInfo_t *db_info; + char buf[BIG_LINE]; + int rv; + int eof; + + buf[0] = 0; + +#ifdef XP_WIN32 + if ((fp = fopen(file, "rt")) == NULL) +#else + if ((fp = fopen(file, "r")) == NULL) +#endif + { + return LDAPU_ERR_CANNOT_OPEN_FILE; + } + + /* Allocate DBConfInfo_t */ + conf_info = (DBConfInfo_t *)malloc(sizeof(DBConfInfo_t)); + + if (!conf_info) { + fclose(fp); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); + + /* Read each db info */ + eof = 0; + while(!eof && + ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) + { + insert_dbconf_dbinfo(conf_info, db_info); + } + + if (rv != LDAPU_SUCCESS) { + dbconf_free_confinfo(conf_info); + *conf_info_out = 0; + } + else { + *conf_info_out = conf_info; + } + + fclose(fp); + return rv; +} + +NSAPI_PUBLIC int dbconf_read_config_file (const char *file, DBConfInfo_t **conf_info_out) +{ + return dbconf_read_config_file_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, + conf_info_out); +} + +int dbconf_read_default_dbinfo_sub (const char *file, + const char *directive, + const int directive_len, + DBConfDBInfo_t **db_info_out) +{ + FILE *fp; + DBConfDBInfo_t *db_info; + char buf[BIG_LINE]; + int rv; + int eof; + + buf[0] = 0; + +#ifdef XP_WIN32 + if ((fp = fopen(file, "rt")) == NULL) +#else + if ((fp = fopen(file, "r")) == NULL) +#endif + { + return LDAPU_ERR_CANNOT_OPEN_FILE; + } + + /* Read each db info until eof or dbname == default*/ + eof = 0; + + while(!eof && + ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) + { + if (!strcmp(db_info->dbname, DBCONF_DEFAULT_DBNAME)) break; + dbconf_free_dbinfo(db_info); + } + + if (rv != LDAPU_SUCCESS) { + *db_info_out = 0; + } + else { + *db_info_out = db_info; + } + + fclose(fp); + return rv; +} + + +NSAPI_PUBLIC int dbconf_read_default_dbinfo (const char *file, + DBConfDBInfo_t **db_info_out) +{ + return dbconf_read_default_dbinfo_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, + db_info_out); +} + +/* + * ldapu_strncasecmp - is like strncasecmp on UNIX but also accepts null strings. + */ +/* Not tested */ +static int ldapu_strncasecmp (const char *s1, const char *s2, size_t len) +{ + int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */ + + if (0 == len) return 0; + else if (!s1) return !s2 ? 0 : 0-tolower(*s2); + else if (!s2) return tolower(*s1); + +#ifdef XP_WIN32 + while(len > 0 && *s1 && *s2 && + (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) + { + s1++; s2++; len--; + } + + if (0 == len) + return 0; + else if (!*s1) + return *s2 ? 0-tolower(*s2) : 0; + else if (!*s2) + return tolower(*s1); + else + return ls1 - ls2; +#else + return strncasecmp(s1, s2, len); +#endif +} + + +/* + * ldapu_strcasecmp - is like strcasecmp on UNIX but also accepts null strings. + */ +int ldapu_strcasecmp (const char *s1, const char *s2) +{ + int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */ + + if (!s1) return !s2 ? 0 : 0-tolower(*s2); + else if (!s2) return tolower(*s1); + +#ifdef XP_WIN32 + while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; } + + if (!*s1) + return *s2 ? 0-tolower(*s2) : 0; + else if (!*s2) + return tolower(*s1); + else + return ls1 - ls2; +#else + return strcasecmp(s1, s2); +#endif +} + +NSAPI_PUBLIC int ldapu_dbinfo_attrval (DBConfDBInfo_t *db_info, + const char *attr, char **val) +{ + /* Look for given attr in the db_info and return its value */ + int rv = LDAPU_ATTR_NOT_FOUND; + DBPropVal_t *next; + + *val = 0; + + if (db_info) { + next = db_info->firstprop; + while (next) { + rv = ldapu_strcasecmp(attr, next->prop); + if (!rv) { + /* Found the property */ + *val = next->val ? strdup(next->val) : 0; + + if (next->val && !*val) { + rv = LDAPU_ERR_OUT_OF_MEMORY; + } + else { + rv = LDAPU_SUCCESS; + } + break; + } + next = next->next; + } + } + + return rv; +} + +void dbconf_print_propval (DBPropVal_t *propval) +{ + if (propval) { + fprintf(stderr, "\tprop: \"%s\"\tval: \"%s\"\n", propval->prop, + propval->val ? propval->val : ""); + } + else { + fprintf(stderr, "Null propval\n"); + } +} + +void dbconf_print_dbinfo (DBConfDBInfo_t *db_info) +{ + DBPropVal_t *next; + + if (db_info) { + fprintf(stderr, "dbname: \"%s\"\n", db_info->dbname); + fprintf(stderr, "url: \t\"%s\"\n", db_info->url ? db_info->url : ""); + next = db_info->firstprop; + while (next) { + dbconf_print_propval(next); + next = next->next; + } + } + else { + fprintf(stderr, "Null db_info\n"); + } +} + +void dbconf_print_confinfo (DBConfInfo_t *conf_info) +{ + DBConfDBInfo_t *next; + + if (conf_info) { + next = conf_info->firstdb; + while (next) { + dbconf_print_dbinfo(next); + next = next->next; + } + } + else { + fprintf(stderr, "Null conf_info\n"); + } +} + + + +NSAPI_PUBLIC int dbconf_output_db_directive (FILE *fp, const char *dbname, + const char *url) +{ + fprintf(fp, "%s %s %s\n", DB_DIRECTIVE, dbname, url); + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int dbconf_output_propval (FILE *fp, const char *dbname, + const char *prop, const char *val, const int encoded) +{ + if (encoded && val && *val) { + char *new_val = dbconf_encodeval(val); + + if (!new_val) return LDAPU_ERR_OUT_OF_MEMORY; + fprintf(fp, "%s:%s %s %s\n", dbname, ENCODED, + prop, new_val); + free(new_val); + } + else { + fprintf(fp, "%s:%s %s\n", dbname, prop, val ? val : ""); + } + + return LDAPU_SUCCESS; +} + + + +NSAPI_PUBLIC int dbconf_get_dbnames (const char *dbmap, char ***dbnames_out, int *cnt_out) +{ + DBConfInfo_t *conf_info = 0; + DBConfDBInfo_t *db = 0; + int cnt = 0; + char **dbnames = 0; + char *heap = 0; + int rv; + + *dbnames_out = 0; + *cnt_out = 0; + + rv = dbconf_read_config_file(dbmap, &conf_info); + + if (rv != LDAPU_SUCCESS) return rv; + + db = conf_info->firstdb; + + + dbnames = (char **)malloc(32*1024); + heap = (char *)dbnames + 2*1024; + + if (!dbnames) { + dbconf_free_confinfo(conf_info); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + *dbnames_out = dbnames; + + while(db) { + *dbnames++ = heap; + strcpy(heap, db->dbname); + heap += strlen(db->dbname)+1; + db = db->next; + cnt++; + } + + *dbnames = NULL; + *cnt_out = cnt; + dbconf_free_confinfo(conf_info); + + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int dbconf_free_dbnames (char **dbnames) +{ + if (dbnames) + free(dbnames); + + return LDAPU_SUCCESS; +} diff --git a/lib/ldaputil/encode.c b/lib/ldaputil/encode.c new file mode 100644 index 00000000..3157418c --- /dev/null +++ b/lib/ldaputil/encode.c @@ -0,0 +1,142 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <malloc.h> +#include <string.h> +#include <ldaputil/certmap.h> +#include <ldaputil/encode.h> + +/* The magic set of 64 chars in the uuencoded data */ +static unsigned char uuset[] = { +'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', +'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n', +'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7', +'8','9','+','/' }; + +static int do_uuencode(unsigned char *src, unsigned char *dst, int srclen) +{ + int i, r; + unsigned char *p; + +/* To uuencode, we snip 8 bits from 3 bytes and store them as +6 bits in 4 bytes. 6*4 == 8*3 (get it?) and 6 bits per byte +yields nice clean bytes + +It goes like this: + AAAAAAAA BBBBBBBB CCCCCCCC +turns into the standard set of uuencode ascii chars indexed by numbers: + 00AAAAAA 00AABBBB 00BBBBCC 00CCCCCC + +Snip-n-shift, snip-n-shift, etc.... + +*/ + + for (p=dst,i=0; i < srclen; i += 3) { + /* Do 3 bytes of src */ + register char b0, b1, b2; + + b0 = src[0]; + if (i==srclen-1) + b1 = b2 = '\0'; + else if (i==srclen-2) { + b1 = src[1]; + b2 = '\0'; + } + else { + b1 = src[1]; + b2 = src[2]; + } + + *p++ = uuset[b0>>2]; + *p++ = uuset[(((b0 & 0x03) << 4) | ((b1 & 0xf0) >> 4))]; + *p++ = uuset[(((b1 & 0x0f) << 2) | ((b2 & 0xc0) >> 6))]; + *p++ = uuset[b2 & 0x3f]; + src += 3; + } + *p = 0; /* terminate the string */ + r = (unsigned char *)p - (unsigned char *)dst;/* remember how many we did */ + + /* Always do 4-for-3, but if not round threesome, have to go + clean up the last extra bytes */ + + for( ; i != srclen; i--) + *--p = '='; + + return r; +} + +const unsigned char pr2six[256]={ + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, + 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, + 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, + 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64 +}; + +static char *_uudecode(const char *bufcoded) +{ + register const char *bufin = bufcoded; + register unsigned char *bufout; + register int nprbytes; + unsigned char *bufplain; + int nbytesdecoded; + + /* Find the length */ + while(pr2six[(int)*(bufin++)] <= 63); + nprbytes = bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes+3)/4) * 3; + + bufout = (unsigned char *) malloc(nbytesdecoded + 1); + bufplain = bufout; + + bufin = bufcoded; + + while (nprbytes > 0) { + *(bufout++) = (unsigned char) + (pr2six[(int)(*bufin)] << 2 | pr2six[(int)bufin[1]] >> 4); + *(bufout++) = (unsigned char) + (pr2six[(int)bufin[1]] << 4 | pr2six[(int)bufin[2]] >> 2); + *(bufout++) = (unsigned char) + (pr2six[(int)bufin[2]] << 6 | pr2six[(int)bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + if(nprbytes & 03) { + if(pr2six[(int)bufin[-2]] > 63) + nbytesdecoded -= 2; + else + nbytesdecoded -= 1; + } + bufplain[nbytesdecoded] = '\0'; + + return (char *)bufplain; +} + + +char *dbconf_encodeval (const char *val) +{ + int len = strlen(val); + char *dst = (char *)malloc(2*len); + + if (dst) { + do_uuencode((unsigned char *)val, (unsigned char *)dst, len); + } + + return dst; +} + +char *dbconf_decodeval (const char *val) +{ + return _uudecode(val); +} + diff --git a/lib/ldaputil/errors.c b/lib/ldaputil/errors.c new file mode 100644 index 00000000..d2f50843 --- /dev/null +++ b/lib/ldaputil/errors.c @@ -0,0 +1,202 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> + +NSAPI_PUBLIC char *ldapu_err2string(int err) +{ + char *rv; + + switch(err) { + + /* Error codes defined in certmap.h */ + case LDAPU_SUCCESS: + rv = "success"; + break; + case LDAPU_FAILED: + rv = "ldap search didn't find an ldap entry"; + break; + case LDAPU_CERT_MAP_FUNCTION_FAILED: + rv = "Cert mapping function failed"; + break; + case LDAPU_CERT_SEARCH_FUNCTION_FAILED: + rv = "Cert search function failed"; + break; + case LDAPU_CERT_VERIFY_FUNCTION_FAILED: + rv = "Cert verify function failed"; + break; + case LDAPU_CERT_MAP_INITFN_FAILED: + rv = "Certmap InitFn function failed"; + break; + + + /* Error codes returned by ldapdb.c */ + case LDAPU_ERR_URL_INVALID_PREFIX: + rv = "invalid local ldap database url prefix -- must be ldapdb://"; + break; + case LDAPU_ERR_URL_NO_BASEDN: + rv = "base dn is missing in ldapdb url"; + break; + case LDAPU_ERR_OUT_OF_MEMORY: + rv = "out of memory"; + break; + case LDAPU_ERR_LDAP_INIT_FAILED: + rv = "Couldn't initialize connection to the ldap directory server"; + break; + case LDAPU_ERR_LCACHE_INIT_FAILED: + rv = "Couldn't initialize connection to the local ldap directory"; + break; + case LDAPU_ERR_LDAP_SET_OPTION_FAILED: + rv = "ldap_set_option failed for local ldap database"; + break; + case LDAPU_ERR_NO_DEFAULT_CERTDB: + rv = "default cert database not initialized when using LDAP over SSL"; + break; + + + /* Errors returned by ldapauth.c */ + case LDAPU_ERR_CIRCULAR_GROUPS: + rv = "Circular groups were detected during group membership check"; + break; + case LDAPU_ERR_INVALID_STRING: + rv = "Invalid string"; + break; + case LDAPU_ERR_INVALID_STRING_INDEX: + rv = "Invalid string index"; + break; + case LDAPU_ERR_MISSING_ATTR_VAL: + rv = "Missing attribute value from the search result"; + break; + + + /* Errors returned by dbconf.c */ + case LDAPU_ERR_CANNOT_OPEN_FILE: + rv = "cannot open the config file"; + break; + case LDAPU_ERR_DBNAME_IS_MISSING: + rv = "database name is missing"; + break; + case LDAPU_ERR_PROP_IS_MISSING: + rv = "database property is missing"; + break; + case LDAPU_ERR_DIRECTIVE_IS_MISSING: + rv = "illegal directive in the config file"; + break; + case LDAPU_ERR_NOT_PROPVAL: + rv = "internal error - LDAPU_ERR_NOT_PROPVAL"; + break; + + + /* Error codes returned by certmap.c */ + case LDAPU_ERR_NO_ISSUERDN_IN_CERT: + rv = "cannot extract issuer DN from the cert"; + break; + case LDAPU_ERR_NO_ISSUERDN_IN_CONFIG_FILE: + rv = "issuer DN missing for non-default certmap"; + break; + case LDAPU_ERR_CERTMAP_INFO_MISSING: + rv = "cert to ldap entry mapping information is missing"; + break; + case LDAPU_ERR_MALFORMED_SUBJECT_DN: + rv = "Found malformed subject DN in the certificate"; + break; + case LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND: + rv = "Certificate couldn't be mapped to an ldap entry"; + break; + case LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN: + rv = "Unable to load certmap plugin library"; + break; + case LDAPU_ERR_MISSING_INIT_FN_IN_CONFIG: + rv = "InitFn must be provided when using certmap plugin library"; + break; + case LDAPU_ERR_MISSING_INIT_FN_IN_LIB: + rv = "Could not find InitFn in the certmap plugin library"; + break; + case LDAPU_ERR_CERT_VERIFY_FAILED: + rv = "Could not matching certificate in User's LDAP entry"; + break; + case LDAPU_ERR_CERT_VERIFY_NO_CERTS: + rv = "User's LDAP entry doesn't have any certificates to compare"; + break; + case LDAPU_ERR_MISSING_LIBNAME: + rv = "Library name is missing in the config file"; + break; + case LDAPU_ERR_MISSING_INIT_FN_NAME: + rv = "Init function name is missing in the config file"; + break; + case LDAPU_ERR_WRONG_ARGS: + rv = "ldaputil API function called with wrong arguments"; + break; + case LDAPU_ERR_RENAME_FILE_FAILED: + rv = "Renaming of file failed"; + break; + case LDAPU_ERR_MISSING_VERIFYCERT_VAL: + rv = "VerifyCert property value must be on or off"; + break; + case LDAPU_ERR_CANAME_IS_MISSING: + rv = "Cert issuer name is missing"; + break; + case LDAPU_ERR_CAPROP_IS_MISSING: + rv = "property name is missing"; + break; + case LDAPU_ERR_UNKNOWN_CERT_ATTR: + rv = "unknown cert attribute"; + break; + + + case LDAPU_ERR_EMPTY_LDAP_RESULT: + rv = "ldap search returned empty result"; + break; + case LDAPU_ERR_MULTIPLE_MATCHES: + rv = "ldap search returned multiple matches when one expected"; + break; + case LDAPU_ERR_MISSING_RES_ENTRY: + rv = "Could not extract entry from the ldap search result"; + break; + case LDAPU_ERR_MISSING_UID_ATTR: + rv = "ldap entry is missing the 'uid' attribute value"; + break; + case LDAPU_ERR_INVALID_ARGUMENT: + rv = "invalid argument passed to the certmap API function"; + break; + case LDAPU_ERR_INVALID_SUFFIX: + rv = "invalid LDAP directory suffix"; + break; + + + /* Error codes returned by cert.c */ + case LDAPU_ERR_EXTRACT_SUBJECTDN_FAILED: + rv = "Couldn't extract the subject DN from the certificate"; + break; + case LDAPU_ERR_EXTRACT_ISSUERDN_FAILED: + rv = "Couldn't extract the issuer DN from the certificate"; + break; + case LDAPU_ERR_EXTRACT_DERCERT_FAILED: + rv = "Couldn't extract the original DER encoding from the certificate"; + break; + + + case LDAPU_ERR_NOT_IMPLEMENTED: + rv = "function not implemented yet"; + break; + case LDAPU_ERR_INTERNAL: + rv = "ldaputil internal error"; + break; + + default: + if (err > 0) { + /* LDAP errors are +ve */ + rv = ldap_err2string(err); + } + else { + rv = "internal error - unknown error code"; + } + break; + } + + return rv; +} diff --git a/lib/ldaputil/examples/Certmap.mak b/lib/ldaputil/examples/Certmap.mak new file mode 100644 index 00000000..618db42b --- /dev/null +++ b/lib/ldaputil/examples/Certmap.mak @@ -0,0 +1,254 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=Certmap - Win32 Debug +!MESSAGE No configuration specified. Defaulting to Certmap - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Certmap - Win32 Release" && "$(CFG)" !=\ + "Certmap - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Certmap.mak" CFG="Certmap - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Certmap - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "Certmap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Certmap - Win32 Debug" +CPP=cl.exe +RSC=rc.exe +MTL=mktyplib.exe + +!IF "$(CFG)" == "Certmap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(OUTDIR)\Certmap.dll" + +CLEAN : + -@erase "$(INTDIR)\init.obj" + -@erase "$(INTDIR)\plugin.obj" + -@erase "$(OUTDIR)\Certmap.dll" + -@erase "$(OUTDIR)\Certmap.exp" + -@erase "$(OUTDIR)\Certmap.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "c:\netscape\suitespot\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "c:\netscape\suitespot\include" /D "WIN32"\ + /D "NDEBUG" /D "_WINDOWS" /Fp"$(INTDIR)/Certmap.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/Certmap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/Certmap.pdb" /machine:I386 /out:"$(OUTDIR)/Certmap.dll"\ + /implib:"$(OUTDIR)/Certmap.lib" +LINK32_OBJS= \ + "$(INTDIR)\init.obj" \ + "$(INTDIR)\plugin.obj" \ + "C:\Netscape\SuiteSpot\lib\nsldap32v10.lib" + +"$(OUTDIR)\Certmap.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Certmap - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(OUTDIR)\Certmap.dll" + +CLEAN : + -@erase "$(INTDIR)\init.obj" + -@erase "$(INTDIR)\plugin.obj" + -@erase "$(INTDIR)\vc40.idb" + -@erase "$(INTDIR)\vc40.pdb" + -@erase "$(OUTDIR)\Certmap.dll" + -@erase "$(OUTDIR)\Certmap.exp" + -@erase "$(OUTDIR)\Certmap.ilk" + -@erase "$(OUTDIR)\Certmap.lib" + -@erase "$(OUTDIR)\Certmap.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "c:\netscape\suitespot\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /MTd /W3 /Gm /GX /Zi /Od /I "c:\netscape\suitespot\include" /D\ + "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fp"$(INTDIR)/Certmap.pch" /YX\ + /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\. +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/Certmap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:yes\ + /pdb:"$(OUTDIR)/Certmap.pdb" /debug /machine:I386 /out:"$(OUTDIR)/Certmap.dll"\ + /implib:"$(OUTDIR)/Certmap.lib" +LINK32_OBJS= \ + "$(INTDIR)\init.obj" \ + "$(INTDIR)\plugin.obj" \ + "C:\Netscape\SuiteSpot\lib\nsldap32v10.lib" + +"$(OUTDIR)\Certmap.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "Certmap - Win32 Release" +# Name "Certmap - Win32 Debug" + +!IF "$(CFG)" == "Certmap - Win32 Release" + +!ELSEIF "$(CFG)" == "Certmap - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\plugin.c +DEP_CPP_PLUGI=\ + ".\plugin.h"\ + "c:\netscape\suitespot\include\certmap.h"\ + "c:\netscape\suitespot\include\lber.h"\ + "c:\netscape\suitespot\include\ldap.h"\ + {$(INCLUDE)}"\sys\types.h"\ + + +"$(INTDIR)\plugin.obj" : $(SOURCE) $(DEP_CPP_PLUGI) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\init.c +DEP_CPP_INIT_=\ + ".\plugin.h"\ + "c:\netscape\suitespot\include\certmap.h"\ + "c:\netscape\suitespot\include\lber.h"\ + "c:\netscape\suitespot\include\ldap.h"\ + {$(INCLUDE)}"\sys\types.h"\ + + +"$(INTDIR)\init.obj" : $(SOURCE) $(DEP_CPP_INIT_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=C:\Netscape\SuiteSpot\lib\nsldap32v10.lib + +!IF "$(CFG)" == "Certmap - Win32 Release" + +!ELSEIF "$(CFG)" == "Certmap - Win32 Debug" + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/lib/ldaputil/examples/Makefile b/lib/ldaputil/examples/Makefile new file mode 100644 index 00000000..4e8b1b4e --- /dev/null +++ b/lib/ldaputil/examples/Makefile @@ -0,0 +1,91 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Makefile for certmap example program. +# + +# +# Please set the ARCH variable to one of the following: +# SOLARIS, IRIX, HPUX +# +ARCH = + + +# +# Please set the SROOT to be same as your server root +# +SROOT = + +# +# Uncomment the following if you need the debug build +# +#COMMON_DEFS = -g + +ifndef ARCH +arch: + @echo "Please edit the Makefile and set the variable: ARCH" + @exit 1 +endif + +ifndef SROOT +sroot: + @echo "Please edit the Makefile and set the server root variable: SROOT" + @exit 1 +endif + +ifeq ($(ARCH), SOLARIS) +CC_CMD = cc -DSOLARIS -D_REENTRANT +LD_SHAREDCMD = ld -G +endif + +ifeq ($(ARCH), IRIX) +CC_CMD = cc +LD_SHAREDCMD = ld -32 -shared +endif + +ifeq ($(ARCH), HPUX) + BIN = certmap.sl +else + BIN = certmap.so +endif + +OBJS = init.o plugin.o + +INCLUDE_FLAGS=-I. -I$(SROOT)/include + +INC_FILES = \ + $(SROOT)/include/certmap.h \ + $(SROOT)/include/ldap.h \ + $(SROOT)/include/lber.h + +all: $(BIN) + +$(INC_FILES): + @echo + @echo "To extend the Certificate to LDAP entry mapping by" + @echo "writing your own functions, you need to download the" + @echo "Certmap API (version 1.0) and LDAP SDK (version 1.0)." + @echo "Please download these from http://???" + @echo "Make sure the following files exist:" + @echo "\t$(SROOT)/include/certmap.h" + @echo "\t$(SROOT)/include/ldap.h" + @echo "\t$(SROOT)/include/lber.h" + @echo + @exit 1 + +$(BIN): $(INC_FILES) $(OBJS) + $(LD_SHAREDCMD) $(OBJS) -o $@ $(EXTRA_LDDEFINES) + +certmap.dll: $(OBJS) + $(LD_SHAREDCMD) $(OBJS) -o $@ $(EXTRA_LDDEFINES) + +.c.o: + $(CC_CMD) $(COMMON_DEFS) $(INCLUDE_FLAGS) -c $< + +clean: + rm -f $(OBJS) certmap.so $(EXTRA_CLEAN) diff --git a/lib/ldaputil/examples/README b/lib/ldaputil/examples/README new file mode 100644 index 00000000..626ef2c9 --- /dev/null +++ b/lib/ldaputil/examples/README @@ -0,0 +1,97 @@ +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +This directory contains an example program to demonstrate +writing plugins using the "Certificate to LDAP Mapping" API. +Please read the "Managing Netscape Servers" manual to find out +about how certificate to ldap mapping can be configured using +the <ServerRoot>/userdb/certmap.conf file. Also refer to the +"Certificate to LDAP Mapping API" documentation to find out +about the various API functions and how you can write your +plugin. + +This example demonstrate use of most of the API functions. It +defines a mapping function, a search function, and a verify +function. Read the API doc to learn about these functions. +The init.c file also contains an init function which sets the +mapping, search and verify functions. + +The Mapping Function +-------------------- + +The mapping function extracts the attributes "CN", "E", "O" and +"C" from the certificate's subject DN using the function +ldapu_get_cert_ava_val. If the attributes "C" doesn't exists +then it defaults to "US". It then gets the value of a custom +certmap.conf property "defaultOU" using the function +ldapu_certmap_info_attrval. This demonstrates how you can have +your own custom properties defined in the certmap.conf file. +The mapping function then returns an ldapdn of the form: +"cn=<name>, ou=<defaultOU>, o=<o>, c=<c>". + +If the "E" attribute has a value, it returns a filter +"mail=<e>". Finally, the mapping function frees the structures +returned by some of the API functions it called. + + +The Search Function +------------------- + +The search function calls a dummy function to get the +certificate's serial number. It then does a subtree search in +the entire directory for the filter +"certSerialNumber=<serial No.>". If this fails, it calls the +default search function. This demonstrates how you can use the +default functions in your custom functions. + +The Verify Function +------------------- + +The verify function returns LDAPU_SUCCESS if only one entry was +returned by the search function. Otherwise, it returns +LDAPU_CERT_VERIFY_FUNCTION_FAILED. + + +Error Reporting +--------------- + +To report errors/warning, there is a function defined called +plugin_ereport. This function demonstrates how to get the +subject DN and the issuer DN from the certificate. + +Build Procedure +--------------- +On UNIX: Edit the Makefile, and set the variables ARCH & SROOT +according to the comments in the Makefile. Download LDAP SDK +from the Netscape's DevEdge site and make the ldap include +files available in <SROOT>/include. Copy the +../include/certmap.h file to the <SROOT>/include directory. +Use 'gmake' to build the plugin. A shared library plugin.so +(plugin.sl on HP) will be created in the current directory. + +On NT: Execute the following command: +NMAKE /f "Certmap.mak" CFG="Certmap - Win32 Debug" +Certmap.dll will be created in the Debug subdirectory. + +Certmap.conf Configuration +-------------------------- +Save a copy of certmap.conf file. +Change the certmap.conf file as follows: + +certmap default default +default:defaultOU marketing +default:library <path to the shared library> +default:InitFn plugin_init_fn + + +After experimenting with this example, restore the old copy of +certmap.conf file. Or else, set the certmap.conf file as follows: + +certmap default default +default:DNComps +default:FilterComps e, mail, uid +default:VerifyCert on diff --git a/lib/ldaputil/examples/init.c b/lib/ldaputil/examples/init.c new file mode 100644 index 00000000..fc606dd9 --- /dev/null +++ b/lib/ldaputil/examples/init.c @@ -0,0 +1,40 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "certmap.h" /* Public Certmap API */ +#include "plugin.h" /* must define extern "C" functions */ + + +NSAPI_PUBLIC int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN, const char *libname) +{ + static int initialized = 0; + int rv; + + /* Make sure CertmapDLLInit is initialized only once */ + if (!initialized) { +#ifdef WIN32 + CertmapDLLInit(rv, libname); + + if (rv != LDAPU_SUCCESS) return rv; +#endif + initialized = 1; + } + + fprintf(stderr, "plugin_init_fn called.\n"); + ldapu_set_cert_mapfn(issuerDN, plugin_mapping_fn); + ldapu_set_cert_verifyfn(issuerDN, plugin_verify_fn); + + if (!default_searchfn) + default_searchfn = ldapu_get_cert_searchfn(issuerDN); + + ldapu_set_cert_searchfn(issuerDN, plugin_search_fn); + return LDAPU_SUCCESS; +} diff --git a/lib/ldaputil/examples/plugin.c b/lib/ldaputil/examples/plugin.c new file mode 100644 index 00000000..4e4adfaf --- /dev/null +++ b/lib/ldaputil/examples/plugin.c @@ -0,0 +1,239 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "certmap.h" /* Public Certmap API */ +#include "plugin.h" /* must define extern "C" functions */ + +#ifdef WIN32 +CertmapDLLInitFnTbl /* Initialize Certmap Function Table */ +#endif + +CertSearchFn_t default_searchfn = 0; + + +/* plugin_ereport - + This function prints an error message to stderr. It prints the issuerDN + and subjectDN alongwith the given message. + */ +static void plugin_ereport (const char *msg, void *cert) +{ + int rv; + char *subjectDN; + char *issuerDN; + char *default_subjectDN = "Failed to get the subject DN"; + char *default_issuerDN = "Failed to get the issuer DN"; + + rv = ldapu_get_cert_subject_dn(cert, &subjectDN); + + if (rv != LDAPU_SUCCESS || !subjectDN) { + subjectDN = default_subjectDN; + } + + rv = ldapu_get_cert_issuer_dn(cert, &issuerDN); + + if (rv != LDAPU_SUCCESS || !issuerDN) { + issuerDN = default_issuerDN; + } + + fprintf(stderr, "%s. Issuer: %s, Subject: %s\n", msg, issuerDN, + subjectDN); + + if (default_subjectDN != subjectDN) ldapu_free(subjectDN); + if (default_issuerDN != issuerDN) ldapu_free(issuerDN); +} + + +/* plugin_mapping_fn - + This mapping function extracts "CN", "O" and "C" attributes from the + subject DN to form ldapDN. It inserts "ou=<defaultOU>" between the + "CN" and the "O" attr-value pair. The <defaultOU> can be configured in + the certmap.conf config file. + If the "C" attr is absent, it defaults to "US". + It extracts the "E" attribute to form the filter. + */ +int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info, + char **ldapDN, char **filter) +{ + char **cn_val; /* get this from the cert */ + char **o_val; /* get this from the cert */ + char **c_val; /* get this from the cert */ + char **e_val; /* get this from the cert */ + char *ou_val; /* get this from the config file */ + int len; + int rv; + + fprintf(stderr, "plugin_mapping_fn called.\n"); + + rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "CN", &cn_val); + + if (rv != LDAPU_SUCCESS || !cn_val) { + plugin_ereport("plugin_mapping_fn: Failed to extract \"CN\" from the cert", cert); + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + + rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "O", &o_val); + + if (rv != LDAPU_SUCCESS || !o_val) { + plugin_ereport("plugin_mapping_fn: Failed to extract \"O\" from the cert", cert); + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + + rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "C", &c_val); + + if (rv != LDAPU_SUCCESS || !c_val) { + plugin_ereport("plugin_mapping_fn: Failed to extract \"C\" from the cert", cert); + } + + rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "E", &e_val); + + if (rv != LDAPU_SUCCESS || !e_val) { + /* Don't return error -- just print the warning */ + plugin_ereport("plugin_mapping_fn: Failed to extract \"E\" from the cert", cert); + } + + /* Get the "OU" from the "defaultOU" property from the config file */ + rv = ldapu_certmap_info_attrval(certmap_info, "defaultOU", &ou_val); + + if (rv != LDAPU_SUCCESS || !ou_val) { + plugin_ereport("plugin_mapping_fn: Failed to get \"defaultOU\" from the configuration", cert); + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + + len = strlen("cn=, ou=, o=, c=") + strlen(cn_val[0]) + strlen(ou_val) + + strlen(o_val[0]) + (c_val ? strlen(c_val[0]) : strlen("US")) + 1; + *ldapDN = (char *)ldapu_malloc(len); + + if (!*ldapDN) { + plugin_ereport("plugin_mapping_fn: Ran out of memory", cert); + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + + if (e_val) { + len = strlen("mail=") + strlen(e_val[0]) + 1; + *filter = (char *)ldapu_malloc(len); + + if (!*filter) { + free(*ldapDN); + plugin_ereport("plugin_mapping_fn: Ran out of memory", cert); + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + sprintf(*filter, "mail=%s", e_val[0]); + } + else { + *filter = 0; + } + + sprintf(*ldapDN, "cn=%s, ou=%s, o=%s, c=%s", cn_val[0], ou_val, + o_val[0], c_val ? c_val[0] : "US"); + + ldapu_free_cert_ava_val(cn_val); + ldapu_free_cert_ava_val(o_val); + ldapu_free_cert_ava_val(c_val); + ldapu_free_cert_ava_val(e_val); + ldapu_free(ou_val); + + fprintf(stderr, "plugin_mapping_fn Returned:\n\tldapDN: \"%s\"\n\tfilter: \"%s\"\n", + *ldapDN, *filter ? *filter : "<NULL>"); + + return LDAPU_SUCCESS; +} + + +int plugin_cert_serial_number (void *cert) +{ + /* Just a stub function. You can get the DER encoded cert by using the + function ldapu_get_cert_der: + */ + unsigned char *derCert; + unsigned int len; + int rv; + int sno; + + rv = ldapu_get_cert_der(cert, &derCert, &len); + + /* extract the serial number from derCert */ + sno = 43534754; /* a fake value for now */ + + ldapu_free((char *)derCert); + + return sno; +} + +/* plugin_search_fn - + This function first does a search based on the cert's serial number. + If that fails, it calls the default search function. + */ +int plugin_search_fn (void *cert, LDAP *ld, void *certmap_info, + const char *suffix, + const char *ldapdn, const char *filter, + const char **attrs, LDAPMessage **res) +{ + int rv; + char snoFilter[256]; + + fprintf(stderr, "plugin_search_fn called.\n"); + sprintf(snoFilter, "certSerialNumber=%d", + plugin_cert_serial_number(cert)); + + /* Search the entire LDAP tree for "certSerialNumber=<serial No.>" */ + rv = ldap_search_s(ld, suffix, LDAP_SCOPE_SUBTREE, snoFilter, + (char **)attrs, 0, res); + + /* ldap_search_s returns LDAP_SUCCESS (rather than LDAPU_SUCCESS) + if there is no error but there may not be any matching entries. + */ + if (rv == LDAP_SUCCESS) { + /* There was no error but check if any entries matched */ + int numEntries = ldap_count_entries(ld, *res); + + if (numEntries > 0) { + /* at least one entry matched */ + /* change the return value to LDAPU_SUCCESS from LDAP_SUCCESS */ + rv = LDAPU_SUCCESS; + } + else { + /* Try the default search function */ + rv = (*default_searchfn)(cert, ld, certmap_info, suffix, ldapdn, + filter, attrs, res); + } + } + + /* It's ok to return the error code from ldap_search_s */ + return rv; +} + +/* + plugin_verify_fn - + This function returns success if only one entry exists in 'res'. + */ +int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info, + LDAPMessage *res, LDAPMessage **entry) +{ + int rv; + int numEntries; + + fprintf(stderr, "plugin_verify_fn called.\n"); + numEntries = ldap_count_entries(ld, res); + + if (numEntries == 1) { + *entry = ldap_first_entry(ld, res); + rv = LDAPU_SUCCESS; + } + else { + plugin_ereport("plugin_verify_fn: Failing because multiple entries matched.", + cert); + *entry = 0; + rv = LDAPU_CERT_VERIFY_FUNCTION_FAILED; + } + + return rv; +} + + diff --git a/lib/ldaputil/examples/plugin.h b/lib/ldaputil/examples/plugin.h new file mode 100644 index 00000000..f73ab377 --- /dev/null +++ b/lib/ldaputil/examples/plugin.h @@ -0,0 +1,33 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _CERTMAP_PLUGIN_H +#define _CERTMAP_PLUGIN_H + +extern CertSearchFn_t default_searchfn; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info, + char **ldapDN, char **filter); + +extern int plugin_search_fn (void *cert, LDAP *ld, void *certmap_info, + const char *basedn, + const char *dn, const char *filter, + const char **attrs, LDAPMessage **res); + +extern int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info, + LDAPMessage *res, LDAPMessage **entry); + +NSAPI_PUBLIC int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN, const char *dllname); + +#ifdef __cplusplus +} +#endif + +#endif /* _CERTMAP_PLUGIN_H */ diff --git a/lib/ldaputil/init.c b/lib/ldaputil/init.c new file mode 100644 index 00000000..8c403e77 --- /dev/null +++ b/lib/ldaputil/init.c @@ -0,0 +1,185 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include <prlink.h> +#include <prio.h> + +/*#include "base/file.h"*/ +#include "ldaputil/certmap.h" +/*#include "ldaputil/ldapdb.h"*/ +#include "ldaputil/ldaputil.h" +#include "ldaputil/cert.h" +#include "ldaputil/errors.h" +#include "ldaputil/init.h" + +#ifdef XP_WIN32 +#define DLL_SUFFIX ".dll" +#ifndef FILE_PATHSEP +#define FILE_PATHSEP '\\' +#endif +#else +#ifndef FILE_PATHSEP +#define FILE_PATHSEP '/' +#endif +#ifdef HPUX +#define DLL_SUFFIX ".sl" +#else +#define DLL_SUFFIX ".so" +#endif +#endif + +static int load_server_libs (const char *dir) +{ + int rv = LDAPU_SUCCESS; + PRDir* ds; + int suffix_len = strlen(DLL_SUFFIX); + + if ((ds = PR_OpenDir(dir)) != NULL) { + PRDirEntry *d; + + /* Dir exists */ + while( (d = PR_ReadDir(ds, PR_SKIP_BOTH)) ) { + PRLibrary *lib = 0; + char *libname = d->name; + int len = strlen(libname); + int is_lib; + + is_lib = (len > suffix_len && !strcmp(libname+len-suffix_len, + DLL_SUFFIX)); + + if(is_lib) { + char path[1024]; + + sprintf(path, "%s%c%s", dir, FILE_PATHSEP, libname); + lib = PR_LoadLibrary(path); + if (!lib) rv = LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN; + } + } + } + else { + /* It's ok if dir doesn't exists */ + } + + return rv; +} + +NSAPI_PUBLIC int ldaputil_init (const char *config_file, + const char *dllname, + const char *serv_root, + const char *serv_type, + const char *serv_id) +{ + int rv = LDAPU_SUCCESS; + static int initialized = 0; + + /* If already initialized, cleanup the old structures */ + if (initialized) ldaputil_exit(); + + if (config_file && *config_file) { + char dir[1024]; + + LDAPUCertMapListInfo_t *certmap_list; + LDAPUCertMapInfo_t *certmap_default; + + if (serv_root && *serv_root) { + /* Load common libraries */ + sprintf(dir, "%s%clib%c%s", serv_root, FILE_PATHSEP, + FILE_PATHSEP, "common"); + rv = load_server_libs(dir); + + if (rv != LDAPU_SUCCESS) return rv; + + if (serv_type && *serv_type) { + /* Load server type specific libraries */ + sprintf(dir, "%s%clib%c%s", serv_root, FILE_PATHSEP, + FILE_PATHSEP, serv_type); + rv = load_server_libs(dir); + + if (rv != LDAPU_SUCCESS) return rv; + + if (serv_id && *serv_id) { + /* Load server instance specific libraries */ + sprintf(dir, "%s%clib%c%s", serv_root, FILE_PATHSEP, + FILE_PATHSEP, serv_id); + rv = load_server_libs(dir); + + if (rv != LDAPU_SUCCESS) return rv; + } + } + } + + rv = ldapu_certmap_init (config_file, dllname, &certmap_list, + &certmap_default); + } + + initialized = 1; + + if (rv != LDAPU_SUCCESS) return rv; + + return rv; +} + +static LDAPUDispatchVector_t __ldapu_vector = { + ldapu_cert_to_ldap_entry, + ldapu_set_cert_mapfn, + ldapu_get_cert_mapfn, + ldapu_set_cert_searchfn, + ldapu_get_cert_searchfn, + ldapu_set_cert_verifyfn, + ldapu_get_cert_verifyfn, + ldapu_get_cert_subject_dn, + ldapu_get_cert_issuer_dn, + ldapu_get_cert_ava_val, + ldapu_free_cert_ava_val, + ldapu_get_cert_der, + ldapu_issuer_certinfo, + ldapu_certmap_info_attrval, + ldapu_err2string, + ldapu_free_old, + ldapu_malloc, + ldapu_strdup, + ldapu_free +}; + +#ifdef XP_UNIX +LDAPUDispatchVector_t *__ldapu_table = &__ldapu_vector; +#endif + +#if 0 +NSAPI_PUBLIC int CertMapDLLInitFn(LDAPUDispatchVector_t **table) +{ + *table = &__ldapu_vector; +} +#endif + +NSAPI_PUBLIC int CertMapDLLInitFn(LDAPUDispatchVector_t **table) +{ + *table = (LDAPUDispatchVector_t *)malloc(sizeof(LDAPUDispatchVector_t)); + + if (!*table) return LDAPU_ERR_OUT_OF_MEMORY; + + (*table)->f_ldapu_cert_to_ldap_entry = ldapu_cert_to_ldap_entry; + (*table)->f_ldapu_set_cert_mapfn = ldapu_set_cert_mapfn; + (*table)->f_ldapu_get_cert_mapfn = ldapu_get_cert_mapfn; + (*table)->f_ldapu_set_cert_searchfn = ldapu_set_cert_searchfn; + (*table)->f_ldapu_get_cert_searchfn = ldapu_get_cert_searchfn; + (*table)->f_ldapu_set_cert_verifyfn = ldapu_set_cert_verifyfn; + (*table)->f_ldapu_get_cert_verifyfn = ldapu_get_cert_verifyfn; + (*table)->f_ldapu_get_cert_subject_dn = ldapu_get_cert_subject_dn; + (*table)->f_ldapu_get_cert_issuer_dn = ldapu_get_cert_issuer_dn; + (*table)->f_ldapu_get_cert_ava_val = ldapu_get_cert_ava_val; + (*table)->f_ldapu_free_cert_ava_val = ldapu_free_cert_ava_val; + (*table)->f_ldapu_get_cert_der = ldapu_get_cert_der; + (*table)->f_ldapu_issuer_certinfo = ldapu_issuer_certinfo; + (*table)->f_ldapu_certmap_info_attrval = ldapu_certmap_info_attrval; + (*table)->f_ldapu_err2string = ldapu_err2string; + (*table)->f_ldapu_free_old = ldapu_free_old; + (*table)->f_ldapu_malloc = ldapu_malloc; + (*table)->f_ldapu_strdup = ldapu_strdup; + (*table)->f_ldapu_free = ldapu_free; + return LDAPU_SUCCESS; +} diff --git a/lib/ldaputil/ldapauth.c b/lib/ldaputil/ldapauth.c new file mode 100644 index 00000000..d8088269 --- /dev/null +++ b/lib/ldaputil/ldapauth.c @@ -0,0 +1,1099 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * ldapauth.cpp: Implements LDAP integration in the web server. + * + * Nitin More, John Kristian + */ + +/* #define DBG_PRINT */ + +#include <stdio.h> /* for BUFSIZ */ +#include <string.h> /* for strncpy, strcat */ +#include <ldap.h> + +#include <ldaputil/certmap.h> +#include <ldaputil/errors.h> +#include <ldaputil/ldapauth.h> + +#include <ldaputili.h> + +/* If we are not interested in the returned attributes, just ask for one + * attribute in the call to ldap_search. Also don't ask for the attribute + * value -- just the attr. + */ +static const char *default_search_attrs[] = { "c" , 0 }; +static int default_search_attrsonly = 1; + +/* + * ldapu_find + * Description: + * Caller should free res if it is not NULL. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * base basedn (where to start the search) + * scope scope for the search. One of + * LDAP_SCOPE_SUBTREE, LDAP_SCOPE_ONELEVEL, and + * LDAP_SCOPE_BASE + * filter LDAP filter + * attrs A NULL-terminated array of strings indicating which + * attributes to return for each matching entry. Passing + * NULL for this parameter causes all available + * attributes to be retrieved. + * attrsonly A boolean value that should be zero if both attribute + * types and values are to be returned, non-zero if only + * types are wanted. + * res A result parameter which will contain the results of + * the search upon completion of the call. + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find (LDAP *ld, const char *base, int scope, + const char *filter, const char **attrs, + int attrsonly, LDAPMessage **res) +{ + int retval; +#ifdef USE_THIS_CODE /* ASYNCHRONOUS */ + int msgid; +#endif + int numEntries; + + *res = 0; + + /* If base is NULL set it to null string */ + if (!base) { + DBG_PRINT1("ldapu_find: basedn is missing -- assuming null string\n"); + base = ""; + } + + if (!filter || !*filter) { + DBG_PRINT1("ldapu_find: filter is missing -- assuming objectclass=*\n"); + filter = ldapu_strings[LDAPU_STR_FILTER_DEFAULT]; + } + + DBG_PRINT2("\tbase:\t\"%s\"\n", base); + DBG_PRINT2("\tfilter:\t\"%s\"\n", filter ? filter : "<NULL>"); + DBG_PRINT2("\tscope:\t\"%s\"\n", + (scope == LDAP_SCOPE_SUBTREE ? "LDAP_SCOPE_SUBTREE" + : (scope == LDAP_SCOPE_ONELEVEL ? "LDAP_SCOPE_ONELEVEL" + : "LDAP_SCOPE_BASE"))); + + retval = ldapu_search_s(ld, base, scope, filter, (char **)attrs, + attrsonly, res); + + if (retval != LDAP_SUCCESS) + { + /* retval = ldap_result2error(ld, *res, 0); */ + DBG_PRINT2("ldapu_search_s: %s\n", ldapu_err2string(retval)); + return(retval); + } + + numEntries = ldapu_count_entries(ld, *res); + + if (numEntries == 1) { + /* success */ + return LDAPU_SUCCESS; + } + else if (numEntries == 0) { + /* not found -- but not an error */ + DBG_PRINT1("ldapu_search_s: Entry not found\n"); + return LDAPU_FAILED; + } + else if (numEntries > 0) { + /* Found more than one entry! */ + DBG_PRINT1("ldapu_search_s: Found more than one entry\n"); + return LDAPU_ERR_MULTIPLE_MATCHES; + } + else { + /* should never get here */ + DBG_PRINT1("ldapu_search_s: should never reach here\n"); + ldapu_msgfree(ld, *res); + return LDAP_OPERATIONS_ERROR; + } +} + + +/* Search function for the cases where base = "" = NULL suffix, that is, search to + * be performed on the entire DIT tree. + * We actually do various searches taking a naming context at a time as the base for + * the search. */ + +int ldapu_find_entire_tree (LDAP *ld, int scope, + const char *filter, const char **attrs, + int attrsonly, LDAPMessage ***res) +{ + int retval = LDAPU_FAILED; + int rv,i, num_namingcontexts; + LDAPMessage *result_entry, *result = NULL; + const char *suffix_attr[2] = {"namingcontexts", NULL}; + /* these are private suffixes that may contain pseudo users + e.g. replication manager that may have certs */ + int num_private_suffix = 1; + const char *private_suffix_list[2] = {"cn=config", NULL}; + char **suffix_list, **suffix = NULL; + + rv = ldapu_find(ld, "",LDAP_SCOPE_BASE, "objectclass=*", suffix_attr, 0, &result); + if (rv != LDAP_SUCCESS) { + if (result) ldapu_msgfree(ld, result); + return rv; + } + + result_entry = ldapu_first_entry(ld, result); + suffix = ldapu_get_values(ld, result_entry, suffix_attr[0]); + suffix_list = suffix; + num_namingcontexts = ldap_count_values(suffix); + /* add private suffixes to our list of suffixes to search */ + if (num_private_suffix) { + suffix_list = ldapu_realloc(suffix_list, + sizeof(char *)*(num_namingcontexts+num_private_suffix+1)); + if (!suffix_list) { + if (result) { + ldapu_msgfree(ld, result); + } + retval = LDAPU_FAILED; + return retval; + } + for (i = num_namingcontexts; i < (num_namingcontexts+num_private_suffix); ++i) { + suffix_list[i] = strdup(private_suffix_list[i-num_namingcontexts]); + } + suffix_list[i] = NULL; + num_namingcontexts += num_private_suffix; + suffix = suffix_list; + } + if (result) ldapu_msgfree(ld, result); + result = 0; + i = 0; + + /* ugaston - the caller function must remember to free the memory allocated here */ + *res = (LDAPMessage **) ldapu_malloc((num_namingcontexts + 1) * sizeof(LDAPMessage *)); + while (suffix && *suffix) { + rv = ldapu_find(ld, *suffix, scope, filter, attrs, attrsonly, &result); + if (scope == LDAP_SCOPE_BASE && rv == LDAP_SUCCESS) { + retval = rv; + (*res)[i++] = result; + break; + } + + switch (rv) { + case LDAP_SUCCESS: + if (retval == LDAP_SUCCESS) { + retval = LDAPU_ERR_MULTIPLE_MATCHES; + (*res)[i++] = result; + break; + } + case LDAPU_ERR_MULTIPLE_MATCHES: + retval = rv; + (*res)[i++] = result; + break; + default: + if (retval != LDAP_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES) { + retval = rv; + } + if (result) ldapu_msgfree(ld, result); + result = 0; + break; + } + + suffix++; + } + + (*res)[i] = NULL; + ldapu_value_free(ld, suffix_list); + return retval; +} + + + +/* + * ldapu_find_uid_attrs + * Description: + * Maps the given uid to a user dn. Caller should free res if it is not + * NULL. Accepts the attrs & attrsonly args. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's name + * base basedn (where to start the search) + * attrs list of attributes to retrieve + * attrsonly flag indicating if attr values are to be retrieved + * res A result parameter which will contain the results of + * the search upon completion of the call. + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_uid_attrs (LDAP *ld, const char *uid, const char *base, + const char **attrs, int attrsonly, + LDAPMessage **res) +{ + int scope = LDAP_SCOPE_SUBTREE; + char filter[ BUFSIZ ]; + int retval; + + /* setup filter as (uid=<uid>) */ + sprintf(filter, ldapu_strings[LDAPU_STR_FILTER_USER], uid); + + retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res); + + return retval; +} + +/* + * ldapu_find_uid + * Description: + * Maps the given uid to a user dn. Caller should free res if it is not + * NULL. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's name + * base basedn (where to start the search) + * res A result parameter which will contain the results of + * the search upon completion of the call. + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_uid (LDAP *ld, const char *uid, const char *base, + LDAPMessage **res) +{ + const char **attrs = 0; /* get all attributes ... */ + int attrsonly = 0; /* ... and their values */ + int retval; + + retval = ldapu_find_uid_attrs(ld, uid, base, attrs, attrsonly, res); + + return retval; +} + +/* + * ldapu_find_userdn + * Description: + * Maps the given uid to a user dn. Caller should free dn if it is not + * NULL. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's name + * base basedn (where to start the search) + * dn user dn + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_userdn (LDAP *ld, const char *uid, const char *base, + char **dn) +{ + LDAPMessage *res = 0; + int retval; + + retval = ldapu_find_uid_attrs(ld, uid, base, default_search_attrs, + default_search_attrsonly, &res); + + if (retval == LDAPU_SUCCESS) { + LDAPMessage *entry; + + entry = ldapu_first_entry(ld, res); + *dn = ldapu_get_dn(ld, entry); + } + else { + *dn = 0; + } + + if (res) ldapu_msgfree(ld, res); + + return retval; +} + +/* + * ldapu_find_group_attrs + * Description: + * Maps the given groupid to a group dn. Caller should free res if it is + * not NULL. Accepts the attrs & attrsonly args. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * groupid Groups's name + * base basedn (where to start the search) + * attrs list of attributes to retrieve + * attrsonly flag indicating if attr values are to be retrieved + * res A result parameter which will contain the results of + * the search upon completion of the call. + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_group_attrs (LDAP *ld, const char *groupid, + const char *base, const char **attrs, + int attrsonly, LDAPMessage **res) +{ + int scope = LDAP_SCOPE_SUBTREE; + char filter[ BUFSIZ ]; + int retval; + + /* setup the filter */ + sprintf(filter, + ldapu_strings[LDAPU_STR_FILTER_GROUP], + groupid); + + retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res); + + return retval; +} + +/* + * ldapu_find_group + * Description: + * Maps the given groupid to a group dn. Caller should free res if it is + * not NULL. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * groupid Groups's name + * base basedn (where to start the search) + * res A result parameter which will contain the results of + * the search upon completion of the call. + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_group (LDAP *ld, const char *groupid, const char *base, + LDAPMessage **res) +{ + const char **attrs = 0; /* get all attributes ... */ + int attrsonly = 0; /* ... and their values */ + int retval; + + retval = ldapu_find_group_attrs (ld, groupid, base, attrs, attrsonly, res); + + return retval; +} + +/* + * ldapu_find_groupdn + * Description: + * Maps the given groupid to a group dn. Caller should free dn if it is + * not NULL. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * groupid Groups's name + * base basedn (where to start the search) + * dn group dn + * Return Values: + * LDAPU_SUCCESS if entry is found + * LDAPU_FAILED if entry is not found + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_find_groupdn (LDAP *ld, const char *groupid, const char *base, + char **dn) +{ + LDAPMessage *res = 0; + int retval; + + retval = ldapu_find_group_attrs(ld, groupid, base, default_search_attrs, + default_search_attrsonly, &res); + + if (retval == LDAPU_SUCCESS) { + LDAPMessage *entry; + + /* get ldap entry */ + entry = ldapu_first_entry(ld, res); + *dn = ldapu_get_dn(ld, entry); + } + else { + *dn = 0; + } + + if (res) ldapu_msgfree(ld, res); + + return retval; +} + + +/* + * continuable_err + * Description: + * Returns true for benign errors (i.e. errors for which recursive + * search can continue. + * Return Values: + * 0 (zero) - if not a benign error + * 1 - if a benign error -- search can continue. + */ +static int continuable_err (int err) +{ + return (err == LDAPU_FAILED); +} + +int ldapu_auth_udn_gdn_recurse (LDAP *ld, const char *userdn, + const char *groupdn, const char *base, + int recurse_cnt) +{ + char filter[ BUFSIZ ]; + const char **attrs = default_search_attrs; + int attrsonly = default_search_attrsonly; + LDAPMessage *res = 0; + int retval; + char member_filter[ BUFSIZ ]; + + if (recurse_cnt >= 30) + return LDAPU_ERR_CIRCULAR_GROUPS; + + /* setup the filter */ + sprintf(member_filter, ldapu_strings[LDAPU_STR_FILTER_MEMBER], userdn, userdn); + + retval = ldapu_find(ld, groupdn, LDAP_SCOPE_BASE, member_filter, attrs, + attrsonly, &res); + + if (res) ldap_msgfree(res); + + if (retval != LDAPU_SUCCESS && continuable_err(retval)) { + LDAPMessage *entry; + + DBG_PRINT2("Find parent groups of \"%s\"\n", userdn); + + /* Modify the filter to include the objectclass check */ + sprintf(filter, ldapu_strings[LDAPU_STR_FILTER_MEMBER_RECURSE], + member_filter); + retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter, + attrs, attrsonly, &res); + + if (retval == LDAPU_SUCCESS || retval == LDAPU_ERR_MULTIPLE_MATCHES) { + /* Found at least one group the userdn is member of */ + + if (!res) { + /* this should never happen */ + retval = LDAPU_ERR_EMPTY_LDAP_RESULT; + } + else { + retval = LDAPU_ERR_MISSING_RES_ENTRY; + + for (entry = ldap_first_entry(ld, res); entry != NULL; + entry = ldap_next_entry(ld, entry)) + { + char *dn = ldap_get_dn(ld, entry); + + retval = ldapu_auth_udn_gdn_recurse(ld, dn, groupdn, + base, recurse_cnt+1); + ldap_memfree(dn); + + if (retval == LDAPU_SUCCESS || !continuable_err(retval)) { + break; + } + } + } + } + + if (res) ldap_msgfree(res); + } + + return retval; +} + +/* + * ldapu_auth_userdn_groupdn: + * Description: + * Checks if the user (userdn) belongs to the given group (groupdn). + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * userdn User's full DN -- actually it could be a group + * dn to check subgroup membership. + * groupdn Group's full DN + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_userdn_groupdn (LDAP *ld, const char *userdn, + const char *groupdn, const char *base) +{ + return ldapu_auth_udn_gdn_recurse(ld, userdn, groupdn, base, 0); +} + + +/* + * ldapu_auth_uid_groupdn: + * Description: + * Similar to ldapu_auth_userdn_groupdn but first maps the uid to a + * full user DN before calling ldapu_auth_userdn_groupdn. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's login name + * groupdn Group's full DN + * base basedn (where to start the search) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_uid_groupdn (LDAP *ld, const char *uid, const char *groupdn, + const char *base) +{ + int retval; + char *dn; + + /* First find userdn for the given uid and + then call ldapu_auth_userdn_groupdn */ + retval = ldapu_find_userdn(ld, uid, base, &dn); + + if (retval == LDAPU_SUCCESS) { + + retval = ldapu_auth_userdn_groupdn(ld, dn, groupdn, base); + ldap_memfree(dn); + } + + return retval; +} + +/* + * ldapu_auth_uid_groupid: + * Description: + * Similar to ldapu_auth_uid_groupdn but first maps the groupid to a + * full group DN before calling ldapu_auth_uid_groupdn. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's login name + * groupid Group's name + * base basedn (where to start the search) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_uid_groupid (LDAP *ld, const char *uid, + const char *groupid, const char *base) +{ + int retval; + char *dn; + + /* First find groupdn for the given groupid and + then call ldapu_auth_uid_groupdn */ + retval = ldapu_find_groupdn(ld, groupid, base, &dn); + + if (retval == LDAPU_SUCCESS) { + retval = ldapu_auth_uid_groupdn(ld, uid, dn, base); + ldapu_memfree(ld, dn); + } + + return retval; +} + +/* + * ldapu_auth_userdn_groupid: + * Description: + * Similar to ldapu_auth_userdn_groupdn but first maps the groupid to a + * full group DN before calling ldapu_auth_userdn_groupdn. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * userdn User's full DN + * groupid Group's name + * base basedn (where to start the search) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_userdn_groupid (LDAP *ld, const char *userdn, + const char *groupid, const char *base) +{ + int retval; + char *groupdn; + + /* First find groupdn for the given groupid and + then call ldapu_auth_userdn_groupdn */ + retval = ldapu_find_groupdn(ld, groupid, base, &groupdn); + + if (retval == LDAPU_SUCCESS) { + retval = ldapu_auth_userdn_groupdn(ld, userdn, groupdn, base); + ldap_memfree(groupdn); + } + + return retval; +} + + +LDAPUStr_t *ldapu_str_alloc (const int size) +{ + LDAPUStr_t *lstr = (LDAPUStr_t *)ldapu_malloc(sizeof(LDAPUStr_t)); + + if (!lstr) return 0; + lstr->size = size < 0 ? 1024 : size; + lstr->str = (char *)ldapu_malloc(lstr->size*sizeof(char)); + lstr->len = 0; + lstr->str[lstr->len] = 0; + + return lstr; +} + + +void ldapu_str_free (LDAPUStr_t *lstr) +{ + if (lstr) { + if (lstr->str) ldapu_free(lstr->str); + ldapu_free((void *)lstr); + } +} + + +int ldapu_str_append(LDAPUStr_t *lstr, const char *arg) +{ + int arglen = strlen(arg); + int len = lstr->len + arglen; + + if (len >= lstr->size) { + /* realloc some more */ + lstr->size += arglen > 4095 ? arglen+1 : 4096; + lstr->str = (char *)ldapu_realloc(lstr->str, lstr->size); + if (!lstr->str) return LDAPU_ERR_OUT_OF_MEMORY; + } + + memcpy((void *)&(lstr->str[lstr->len]), (void *)arg, arglen); + lstr->len += arglen; + lstr->str[lstr->len] = 0; + return LDAPU_SUCCESS; +} + + +/* + * ldapu_auth_userdn_groupids_recurse: + * Description: + * Checks if the user is member of the given comma separated list of + * group names. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * filter filter to use in the search + * groupids some representation of group names. Example, + * a comma separated names in a string, hash + * table, etc. This function doesn't need to + * know the name of the groups. It calls the + * following function to check if one of the + * groups returned by the search is in the list. + * grpcmpfn group name comparison function. + * base basedn (where to start the search) + * recurse_cnt recursion count to detect circular groups + * group_out if successful, pointer to the user's group + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of one of the groups + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +static int ldapu_auth_userdn_groupids_recurse (LDAP *ld, const char *filter, + void *groupids, + LDAPU_GroupCmpFn_t grpcmpfn, + const char *base, + int recurse_cnt, + char **group_out) +{ + LDAPMessage *res = 0; + const char *attrs[] = { "CN", 0 }; + int attrsonly = 0; + LDAPMessage *entry; + int rv; + int retval; + int i; + int done; + + if (recurse_cnt >= 30) + return LDAPU_ERR_CIRCULAR_GROUPS; + + /* Perform the ldap lookup */ + retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter, attrs, + attrsonly, &res); + + if (retval != LDAPU_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES ) { + /* user is not a member of any group */ + if (res) ldap_msgfree(res); + return retval; + } + + retval = LDAPU_FAILED; + done = 0; + + /* check if one of the matched groups is one of the given groups */ + for (entry = ldap_first_entry(ld, res); entry != NULL && !done; + entry = ldap_next_entry(ld, entry)) + { + struct berval **bvals; + + if ((bvals = ldap_get_values_len(ld, entry, "CN")) == NULL) { + /* This shouldn't happen */ + retval = LDAPU_ERR_MISSING_ATTR_VAL; + continue; + } + + /* "CN" may have multiple values */ + /* Check each value of "CN" against the 'groupids' */ + for ( i = 0; bvals[i] != NULL; i++ ) { + rv = (*grpcmpfn)(groupids, bvals[i]->bv_val, bvals[i]->bv_len); + if (rv == LDAPU_SUCCESS) { + char *group = (char *)ldapu_malloc(bvals[i]->bv_len+1); + + if (!group) { + retval = LDAPU_ERR_OUT_OF_MEMORY; + } + else { + strncpy(group, bvals[i]->bv_val, bvals[i]->bv_len); + group[bvals[i]->bv_len] = 0; + *group_out = group; + retval = LDAPU_SUCCESS; + } + done = 1; /* exit from the outer loop too */ + break; + } + } + + ldap_value_free_len(bvals); + } + + if (retval == LDAPU_FAILED) { + /* None of the matched groups is in 'groupids' */ + /* Perform the nested group membership check */ + LDAPUStr_t *filter1; + LDAPUStr_t *filter2; + char *rfilter = 0; + int rlen; + /* Finally we need a filter which looks like: + (| (& (objectclass=groupofuniquenames) + (| (uniquemember=<grp1dn>)(uniquemember=<grp2dn>) ...)) + (& (objectclass=groupofnames) + (| (member=<grp1dn>)(member=<grp2dn>) ...))) + Construct 2 sub-filters first as follows: + (uniquemember=<grp1dn>)(uniquemember=<grp2dn>)... AND + (member=<grp1dn>)(member=<grp2dn>)... + Then insert them in the main filter. + */ + filter1 = ldapu_str_alloc(1024); + filter2 = ldapu_str_alloc(1024); + if (!filter1 || !filter2) return LDAPU_ERR_OUT_OF_MEMORY; + rv = LDAPU_SUCCESS; + + for (entry = ldap_first_entry(ld, res); entry != NULL; + entry = ldap_next_entry(ld, entry)) + { + char *dn = ldap_get_dn(ld, entry); + if (((rv = ldapu_str_append(filter1, "(uniquemember=")) + != LDAPU_SUCCESS) || + ((rv = ldapu_str_append(filter1, dn)) != LDAPU_SUCCESS) || + ((rv = ldapu_str_append(filter1, ")")) != LDAPU_SUCCESS) || + ((rv = ldapu_str_append(filter2, "(member=")) + != LDAPU_SUCCESS) || + ((rv = ldapu_str_append(filter2, dn)) != LDAPU_SUCCESS) || + ((rv = ldapu_str_append(filter2, ")")) != LDAPU_SUCCESS)) + { + ldap_memfree(dn); + break; + } + ldap_memfree(dn); + } + + if (rv != LDAPU_SUCCESS) { + /* something went wrong in appending to filter1 or filter2 */ + ldapu_str_free(filter1); + ldapu_str_free(filter2); + retval = rv; + } + else { + /* Insert the 2 filters in the main filter */ + rlen = filter1->len + filter2->len + + strlen("(| (& (objectclass=groupofuniquenames)" + "(| ))" + "(& (objectclass=groupofnames)" + "(| )))") + 1; + rfilter = (char *)ldapu_malloc(rlen); + if (!rfilter) return LDAPU_ERR_OUT_OF_MEMORY; + sprintf(rfilter, + "(| (& (objectclass=groupofuniquenames)" + "(| %s))" + "(& (objectclass=groupofnames)" + "(| %s)))", + filter1->str, filter2->str); + ldapu_str_free(filter1); + ldapu_str_free(filter2); + retval = ldapu_auth_userdn_groupids_recurse(ld, rfilter, groupids, + grpcmpfn, base, + ++recurse_cnt, + group_out); + ldapu_free(rfilter); + } + + } + + if (res) ldap_msgfree(res); + return retval; +} + +/* + * ldapu_auth_userdn_groupids: + * Description: + * Checks if the user is member of the given comma separated list of + * group names. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * userdn User's full DN + * groupids some representation of group names. Example, + * a comma separated names in a string, hash + * table, etc. This function doesn't need to + * know the name of the groups. It calls the + * following function to check if one of the + * groups returned by the search is in the list. + * grpcmpfn group name comparison function. + * base basedn (where to start the search) + * group_out if successful, pointer to the user's group + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of one of the groups + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_userdn_groupids (LDAP *ld, const char *userdn, + void *groupids, + LDAPU_GroupCmpFn_t grpcmpfn, + const char *base, + char **group_out) +{ + char *filter; + int len; + int rv; + + *group_out = 0; + /* allocate a big enough filter */ + /* The filter looks like: + (| (& (objectclass=groupofuniquenames)(uniquemember=<userdn>)) + (& (objectclass=groupofnames)(member=<userdn>))) + */ + + len = 2 * strlen(userdn) + 1 + + strlen("(| (& (objectclass=groupofuniquenames)(uniquemember=))" + "(& (objectclass=groupofnames)(member=)))"); + filter = (char *)ldapu_malloc(len); + + if (!filter) return LDAPU_ERR_OUT_OF_MEMORY; + + sprintf(filter, "(| (& (objectclass=groupofuniquenames)(uniquemember=%s))" + "(& (objectclass=groupofnames)(member=%s)))", + userdn, userdn); + + rv = ldapu_auth_userdn_groupids_recurse(ld, filter, groupids, + grpcmpfn, base, + 0, group_out); + + ldapu_free(filter); + return rv; +} + + +/* + * ldapu_auth_userdn_attrfilter: + * Description: + * Checks if the user's entry has the given attributes + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * userdn User's full DN + * attrfilter attribute filter + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_userdn_attrfilter (LDAP *ld, const char *userdn, + const char *attrfilter) +{ + const char *base = userdn; + int scope = LDAP_SCOPE_BASE; + const char *filter = attrfilter; + const char **attrs = default_search_attrs; + int attrsonly = default_search_attrsonly; + LDAPMessage *res = 0; + int retval; + + retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res); + + if (res) ldapu_msgfree(ld, res); + + return retval; +} + +/* + * ldapu_auth_uid_attrfilter: + * Description: + * Checks if the user's entry has the given attributes. First maps + the uid to userdn. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's name + * attrfilter attribute filter + * base basedn (where to start the search) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user is member of the group + * LDAPU_FAILED if user is not a member of the group + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_uid_attrfilter (LDAP *ld, const char *uid, const char *attrfilter, + const char *base) +{ + int scope = LDAP_SCOPE_SUBTREE; + char filter[ BUFSIZ ]; + const char **attrs = default_search_attrs; + int attrsonly = default_search_attrsonly; + LDAPMessage *res = 0; + int retval; + + /* setup filter as (& (uid=<uid>) (attrfilter)) */ + if (*attrfilter == '(') + sprintf(filter, "(& (uid=%s) %s)", uid, attrfilter); + else + sprintf(filter, "(& (uid=%s) (%s))", uid, attrfilter); + + retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res); + + if (res) ldapu_msgfree(ld, res); + + return retval; +} + +/* + * ldapu_auth_userdn_password: + * Description: + * Checks the user's password against LDAP by binding using the + * userdn and the password. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * userdn User's full DN + * password User's password (clear text) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user credentials are valid + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_userdn_password (LDAP *ld, const char *userdn, const char *password) +{ + LDAPMessage *res = 0; + int retval; + + DBG_PRINT2("\tuserdn:\t\"%s\"\n", userdn); + DBG_PRINT2("\tpassword:\t\"%s\"\n", password); + + retval = ldap_simple_bind_s(ld, userdn, password); + + if (retval != LDAP_SUCCESS) + { + DBG_PRINT2("ldap_simple_bind_s: %s\n", ldap_err2string(retval)); + return(retval); + } + + return LDAPU_SUCCESS; +} + +/* + * ldapu_auth_uid_password: + * Description: + * First converts the uid to userdn and calls + * ldapu_auth_userdn_password. + * Arguments: + * ld Pointer to LDAP (assumes connection has been + * established and the client has called the + * appropriate bind routine) + * uid User's name + * password User's password (clear text) + * Return Values: (same as ldapu_find) + * LDAPU_SUCCESS if user credentials are valid + * <rv> if error, where <rv> can be passed to + * ldap_err2string to get an error string. + */ +int ldapu_auth_uid_password (LDAP *ld, const char *uid, + const char *password, const char *base) +{ + int retval; + char *dn; + + /* First find userdn for the given uid and + then call ldapu_auth_userdn_password */ + retval = ldapu_find_userdn(ld, uid, base, &dn); + + if (retval == LDAPU_SUCCESS) { + retval = ldapu_auth_userdn_password(ld, dn, password); + ldapu_memfree(ld, dn); + } + + return retval; +} + + +/* ldapu_string_set -- + * This function is not tested yet for its usefulness. This is to be used to + * customize the strings used in the LDAP searches performed through + * 'ldaputil'. This could also be extended to setting customized error + * messages (and even i18n equivalent of error messages). + */ +NSAPI_PUBLIC int ldapu_string_set (const int type, const char *filter) +{ + if (!filter || !*filter) return LDAPU_ERR_INVALID_STRING; + + if (type < 0 || type >= LDAPU_STR_MAX_INDEX) + return LDAPU_ERR_INVALID_STRING_INDEX; + + ldapu_strings[type] = strdup(filter); + + if (!ldapu_strings[type]) return LDAPU_ERR_OUT_OF_MEMORY; + + return LDAPU_SUCCESS; +} + + +NSAPI_PUBLIC const char *ldapu_string_get (const int type) +{ + if (type < 0 || type >= LDAPU_STR_MAX_INDEX) + return 0; + + return ldapu_strings[type]; +} diff --git a/lib/ldaputil/ldapdb.c b/lib/ldaputil/ldapdb.c new file mode 100644 index 00000000..2dfc30ca --- /dev/null +++ b/lib/ldaputil/ldapdb.c @@ -0,0 +1,583 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef DONT_USE_LDAP_SSL +#define USE_LDAP_SSL +#endif + + +#include <string.h> +#include <malloc.h> + +#ifdef LDAPDB_THREAD_SAFE +#include <nspr.h> +#include <prthread.h> +#include <prmon.h> +/* removed for new ns security integration +#include <xp_error.h> +*/ +#endif /* LDAPDB_THREAD_SAFE */ + +#include "ldaputil/errors.h" +#include "ldaputil/certmap.h" +#include "ldaputil/ldapdb.h" + +#ifdef USE_LDAP_SSL +/* removed for new ns security integration +#include <sec.h> +#include <key.h> +#include "cert.h" +*/ +#include <ssl.h> +#include "ldap_ssl.h" +#endif + +#include "ldaputili.h" + +#define LDAPDB_PREFIX_WITH_SLASHES "ldapdb://" +#define LDAPDB_PREFIX_WITH_SLASHES_LEN 9 + +#ifndef LDAPDB_THREAD_SAFE +#define ldb_crit_init(x) +#define ldb_crit_enter(x) +#define ldb_crit_exit(x) +#else +#ifdef NSPR20 +uintn tsdindex; +#else +int32 tsdindex; +#endif + +static void ldb_crit_init (LDAPDatabase_t *ldb) +{ + ldb->crit = PR_NewMonitor(); +} + +static void ldb_crit_enter (LDAPDatabase_t *ldb) +{ + PR_EnterMonitor(ldb->crit); +} + +static void ldb_crit_exit (LDAPDatabase_t *ldb) +{ + PR_ExitMonitor(ldb->crit); +} + +struct ldap_error { + int le_errno; + char *le_matched; + char *le_errmsg; +}; + + +static void set_ld_error( int err, char *matched, char *errmsg, void *dummy ) +{ + struct ldap_error *le; + +#ifdef NSPR20 + if (!(le = (struct ldap_error *) PR_GetThreadPrivate(tsdindex))) { + le = (struct ldap_error *) malloc(sizeof(struct ldap_error)); + memset((void *)le, 0, sizeof(struct ldap_error)); + PR_SetThreadPrivate(tsdindex, (void *)le); + } +#else + le = (struct ldap_error *) PR_GetThreadPrivate( PR_CurrentThread(), tsdindex ); +#endif + le->le_errno = err; + if ( le->le_matched != NULL ) { + ldap_memfree( le->le_matched ); + } + le->le_matched = matched; + if ( le->le_errmsg != NULL ) { + ldap_memfree( le->le_errmsg ); + } + le->le_errmsg = errmsg; +} + +static int get_ld_error( char **matched, char **errmsg, void *dummy ) +{ + struct ldap_error *le; + +#ifdef NSPR20 + le = (struct ldap_error *) PR_GetThreadPrivate( tsdindex); +#else + le = (struct ldap_error *) PR_GetThreadPrivate( PR_CurrentThread(), tsdindex ); +#endif + if ( matched != NULL ) { + *matched = le->le_matched; + } + if ( errmsg != NULL ) { + *errmsg = le->le_errmsg; + } + return( le->le_errno ); +} + +static void set_errno( int err ) +{ + PR_SetError( err, 0); +} + +static int get_errno( void ) +{ + return( PR_GetError() ); +} + +#ifdef LDAP_OPT_DNS_FN_PTRS /* not supported in older LDAP SDKs */ +static LDAPHostEnt * +ldapu_copyPRHostEnt2LDAPHostEnt( LDAPHostEnt *ldhp, PRHostEnt *prhp ) +{ + ldhp->ldaphe_name = prhp->h_name; + ldhp->ldaphe_aliases = prhp->h_aliases; + ldhp->ldaphe_addrtype = prhp->h_addrtype; + ldhp->ldaphe_length = prhp->h_length; + ldhp->ldaphe_addr_list = prhp->h_addr_list; + return( ldhp ); +} + +static LDAPHostEnt * +ldapu_gethostbyname( const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ) +{ + PRHostEnt prhent; + + if( !statusp || ( *statusp = (int)PR_GetHostByName( name, buffer, + buflen, &prhent )) == PR_FAILURE ) { + return( NULL ); + } + + return( ldapu_copyPRHostEnt2LDAPHostEnt( result, &prhent )); +} + +static LDAPHostEnt * +ldapu_gethostbyaddr( const char *addr, int length, int type, + LDAPHostEnt *result, char *buffer, int buflen, int *statusp, + void *extradata ) +{ + return( (LDAPHostEnt *)PR_GetError() ); +} +#endif /* LDAP_OPT_DNS_FN_PTRS */ +#endif /* LDAPDB_THREAD_SAFE */ + + +static void unescape_ldap_basedn (char *str) +{ + if (strchr(str, '%')) { + register int x = 0, y = 0; + int l = strlen(str); + char digit; + + while(x < l) { + if((str[x] == '%') && (x < (l - 2))) { + ++x; + digit = (str[x] >= 'A' ? + ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0')); + digit *= 16; + + ++x; + digit += (str[x] >= 'A' ? + ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0')); + + str[y] = digit; + } + else { + str[y] = str[x]; + } + x++; + y++; + } + str[y] = '\0'; + } +} + +/* + * extract_path_and_basedn: + * Description: + * Parses the ldapdb url and returns pathname to the lcache.conf file + * and basedn. The caller must free the memory allocated for the + * returned path and the basedn. + * Arguments: + * url URL (must begin with ldapdb://) + * path Pathname to the lcache.conf file + * basedn basedn for the ldapdb. + * Return Values: (same as ldap_find) + * LDAPU_SUCCESS if the URL is parsed successfully + * <rv> if error, one of the LDAPU_ errors. + */ +static int extract_path_and_basedn(const char *url_in, char **path_out, + char **basedn_out) +{ + char *url = strdup(url_in); + char *basedn; + char *path; + + *path_out = 0; + *basedn_out = 0; + + if (!url) return LDAPU_ERR_OUT_OF_MEMORY; + + if (strncmp(url, LDAPDB_URL_PREFIX, LDAPDB_URL_PREFIX_LEN)) { + free(url); + return LDAPU_ERR_URL_INVALID_PREFIX; + } + + path = url + LDAPDB_URL_PREFIX_LEN; + + if (strncmp(path, "//", 2)) { + free(url); + return LDAPU_ERR_URL_INVALID_PREFIX; + } + + path += 2; + + /* Find base DN -- empty string is OK */ + if ((basedn = strrchr(path, '/')) == NULL) { + free(url); + return LDAPU_ERR_URL_NO_BASEDN; + } + + *basedn++ = '\0'; /* terminate the path */ + unescape_ldap_basedn(basedn); + *basedn_out = strdup(basedn); + *path_out = strdup(path); + free(url); + return (*basedn_out && *path_out) ? LDAPU_SUCCESS : LDAPU_ERR_OUT_OF_MEMORY; +} + +NSAPI_PUBLIC int ldapu_ldapdb_url_parse (const char *url, + LDAPDatabase_t **ldb_out) +{ + char *path = 0; + char *basedn = 0; + LDAPDatabase_t *ldb = 0; + int rv; + + rv = extract_path_and_basedn(url, &path, &basedn); + + if (rv != LDAPU_SUCCESS) { + if (path) free(path); + if (basedn) free(basedn); + return rv; + } + + ldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t)); + + if (!ldb) { + if (path) free(path); + if (basedn) free(basedn); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + memset((void *)ldb, 0, sizeof(LDAPDatabase_t)); + ldb->basedn = basedn; /* extract_path_and_basedn has allocated */ + ldb->host = path; /* memory for us -- don't make a copy */ + ldb_crit_init(ldb); + *ldb_out = ldb; + + return LDAPU_SUCCESS; +} + + +/* + * ldapu_url_parse: + * Description: + * Parses the ldapdb or ldap url and returns a LDAPDatabase_t struct + * Arguments: + * url URL (must begin with ldapdb://) + * binddn DN to use to bind to ldap server. + * bindpw Password to use to bind to ldap server. + * ldb a LDAPDatabase_t struct filled from parsing + * the url. + * Return Values: (same as ldap_find) + * LDAPU_SUCCESS if the URL is parsed successfully + * <rv> if error, one of the LDAPU_ errors. + */ +NSAPI_PUBLIC int ldapu_url_parse (const char *url, const char *binddn, + const char *bindpw, + LDAPDatabase_t **ldb_out) +{ + LDAPDatabase_t *ldb; + LDAPURLDesc *ludp = 0; + int rv; + + *ldb_out = 0; + + if (!strncmp(url, LDAPDB_PREFIX_WITH_SLASHES, + LDAPDB_PREFIX_WITH_SLASHES_LEN)) + { + return ldapu_ldapdb_url_parse(url, ldb_out); + } + + /* call ldapsdk's parse function */ + rv = ldap_url_parse((char *)url, &ludp); + + if (rv != LDAP_SUCCESS) { + if (ludp) ldap_free_urldesc(ludp); + return LDAPU_ERR_URL_PARSE_FAILED; + } + + ldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t)); + + if (!ldb) { + ldap_free_urldesc(ludp); + return LDAPU_ERR_OUT_OF_MEMORY; + } + + memset((void *)ldb, 0, sizeof(LDAPDatabase_t)); + ldb->host = ludp->lud_host ? strdup(ludp->lud_host) : 0; + ldb->use_ssl = ludp->lud_options & LDAP_URL_OPT_SECURE; + ldb->port = ludp->lud_port ? ludp->lud_port : ldb->use_ssl ? 636 : 389; + ldb->basedn = ludp->lud_dn ? strdup(ludp->lud_dn) : 0; + ldb_crit_init(ldb); + ldap_free_urldesc(ludp); + + if (binddn) ldb->binddn = strdup(binddn); + + if (bindpw) ldb->bindpw = strdup(bindpw); + + /* success */ + *ldb_out = ldb; + + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC void ldapu_free_LDAPDatabase_t (LDAPDatabase_t *ldb) +{ + if (ldb->host) free(ldb->host); + if (ldb->basedn) free(ldb->basedn); + if (ldb->filter) free(ldb->filter); + if (ldb->binddn) free(ldb->binddn); + if (ldb->bindpw) free(ldb->bindpw); + if (ldb->ld) ldapu_unbind(ldb->ld); + memset((void *)ldb, 0, sizeof(LDAPDatabase_t)); + free(ldb); +} + +NSAPI_PUBLIC LDAPDatabase_t *ldapu_copy_LDAPDatabase_t (const LDAPDatabase_t *ldb) +{ + LDAPDatabase_t *nldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t)); + + if (!nldb) return 0; + + memset((void *)nldb, 0, sizeof(LDAPDatabase_t)); + nldb->use_ssl = ldb->use_ssl; + if (ldb->host) nldb->host = strdup(ldb->host); + nldb->port = ldb->port; + if (ldb->basedn) nldb->basedn = strdup(ldb->basedn); + nldb->scope = ldb->scope; + if (ldb->filter) nldb->filter = strdup(ldb->filter); + nldb->ld = 0; + if (ldb->binddn) nldb->binddn = strdup(ldb->binddn); + if (ldb->bindpw) nldb->bindpw = strdup(ldb->bindpw); + nldb->bound = 0; + ldb_crit_init(nldb); + + return nldb; +} + +NSAPI_PUBLIC int ldapu_is_local_db (const LDAPDatabase_t *ldb) +{ + return ldb->port ? 0 : 1; +} + +static int LDAP_CALL LDAP_CALLBACK +ldapu_rebind_proc (LDAP *ld, char **whop, char **passwdp, + int *authmethodp, int freeit, void *arg) +{ + if (freeit == 0) { + LDAPDatabase_t *ldb = (LDAPDatabase_t *)arg; + *whop = ldb->binddn; + *passwdp = ldb->bindpw; + *authmethodp = LDAP_AUTH_SIMPLE; + } + + return LDAP_SUCCESS; +} + +NSAPI_PUBLIC int ldapu_ldap_init(LDAPDatabase_t *ldb) +{ + LDAP *ld = 0; + + ldb_crit_enter(ldb); + +#ifdef USE_LDAP_SSL + /* Note. This assume the security related initialization is done */ + /* The step needed is : + PR_Init + RNG_SystemInfoForRNG + RNG_RNGInit + CERT_OpenCertDBFilename + CERT_SetDefaultCertDB + SECMOD_init + + And because ldapssl_init depends on security initialization, it is + no good for non-ssl init + */ + if (ldb->use_ssl) + ld = ldapssl_init(ldb->host, ldb->port, ldb->use_ssl); + else ldap_init(ldb->host, ldb->port); +#else + ld = ldapu_init(ldb->host, ldb->port); +#endif + + if (ld == NULL) { + DBG_PRINT1("ldapu_ldap_init: Failed to initialize connection"); + ldb_crit_exit(ldb); + return LDAPU_ERR_LDAP_INIT_FAILED; + } + +#ifdef LDAPDB_THREAD_SAFE + { + struct ldap_thread_fns tfns; + +#ifdef NSPR20 + PR_NewThreadPrivateIndex(&tsdindex, NULL); +#else + tsdindex = PR_NewThreadPrivateID(); +#endif + + /* set mutex pointers */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_mutex_alloc = (void *(*)(void))PR_NewMonitor; + tfns.ltf_mutex_free = (void (*)(void *))PR_DestroyMonitor; + tfns.ltf_mutex_lock = (int (*)(void *)) PR_EnterMonitor; + tfns.ltf_mutex_unlock = (int (*)(void *)) PR_ExitMonitor; + tfns.ltf_get_errno = get_errno; + tfns.ltf_set_errno = set_errno; + tfns.ltf_get_lderrno = get_ld_error; + tfns.ltf_set_lderrno = set_ld_error; + /* set ld_errno pointers */ + if ( ldapu_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *) &tfns ) + != 0 ) { + ldb_crit_exit(ldb); + return LDAPU_ERR_LDAP_SET_OPTION_FAILED; + } + } +#ifdef LDAP_OPT_DNS_FN_PTRS /* not supported in older LDAP SDKs */ + { + /* install DNS functions */ + struct ldap_dns_fns dnsfns; + memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) ); + dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE; + dnsfns.lddnsfn_gethostbyname = ldapu_gethostbyname; + dnsfns.lddnsfn_gethostbyaddr = ldapu_gethostbyaddr; + if ( ldapu_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns ) + != 0 ) { + ldb_crit_exit(ldb); + return LDAPU_ERR_LDAP_SET_OPTION_FAILED; + } + } +#endif /* LDAP_OPT_DNS_FN_PTRS */ +#endif /* LDAPDB_THREAD_SAFE */ + + if (ldapu_is_local_db(ldb)) { + /* No more Local db support, force error! */ + return LDAPU_ERR_LCACHE_INIT_FAILED; +#if 0 + int optval = 1; + + if (lcache_init(ld, ldb->host) != 0) { + ldb_crit_exit(ldb); + return LDAPU_ERR_LCACHE_INIT_FAILED; + } + + if (ldap_set_option(ld, LDAP_OPT_CACHE_ENABLE, &optval) != 0) { + ldb_crit_exit(ldb); + return LDAPU_ERR_LDAP_SET_OPTION_FAILED; + } + + optval = LDAP_CACHE_LOCALDB; + + if (ldap_set_option(ld, LDAP_OPT_CACHE_STRATEGY, &optval) != 0) { + ldb_crit_exit(ldb); + return LDAPU_ERR_LDAP_SET_OPTION_FAILED; + } +#endif + } + else if (ldb->binddn && *ldb->binddn) { + /* Set the rebind proc */ + /* Rebind proc is used when chasing a referral */ + ldap_set_rebind_proc(ld, ldapu_rebind_proc, (void *)ldb); + } + + ldb->ld = ld; + ldb_crit_exit(ldb); + + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int ldapu_ldap_rebind (LDAPDatabase_t *ldb) +{ + int retry; + int rv = LDAPU_FAILED; + + ldb_crit_enter(ldb); + + if (ldb->ld) { + retry = (ldb->bound != -1 ? 1 : 0); /* avoid recursion */ + +#ifdef USE_LDAP_SSL + if (ldb->use_ssl && !CERT_GetDefaultCertDB()) { + /* default cert database has not been initialized */ + rv = LDAPU_ERR_NO_DEFAULT_CERTDB; + } + else +#endif + { + rv = ldap_simple_bind_s(ldb->ld, ldb->binddn, ldb->bindpw); + } + + /* retry once if the LDAP server is down */ + if (rv == LDAP_SERVER_DOWN && retry) { + ldb->bound = -1; /* to avoid recursion */ + rv = ldapu_ldap_reinit_and_rebind(ldb); + } + + if (rv == LDAPU_SUCCESS) ldb->bound = 1; + } + + ldb_crit_exit(ldb); + + return rv; +} + +NSAPI_PUBLIC int ldapu_ldap_init_and_bind (LDAPDatabase_t *ldb) +{ + int rv = LDAPU_SUCCESS; + + ldb_crit_enter(ldb); + + if (!ldb->ld) { + rv = ldapu_ldap_init(ldb); + /* ldb->bound may be set to -1 to avoid recursion */ + if (ldb->bound == 1) ldb->bound = 0; + } + + /* bind as binddn & bindpw if not bound already */ + if (rv == LDAPU_SUCCESS && ldb->bound != 1) { + rv = ldapu_ldap_rebind (ldb); + } + + ldb_crit_exit(ldb); + + return rv; +} + +NSAPI_PUBLIC int ldapu_ldap_reinit_and_rebind (LDAPDatabase_t *ldb) +{ + int rv; + + ldb_crit_enter(ldb); + + if (ldb->ld) { + ldapu_unbind(ldb->ld); + ldb->ld = 0; + } + + rv = ldapu_ldap_init_and_bind(ldb); + ldb_crit_exit(ldb); + return rv; +} + diff --git a/lib/ldaputil/ldapu-changes.html b/lib/ldaputil/ldapu-changes.html new file mode 100644 index 00000000..124181f5 --- /dev/null +++ b/lib/ldaputil/ldapu-changes.html @@ -0,0 +1,403 @@ +<!-- BEGIN COPYRIGHT BLOCK + Copyright 2001 Sun Microsystems, Inc. + Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + All rights reserved. + END COPYRIGHT BLOCK --> +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="Nitin More"> + <META NAME="GENERATOR" CONTENT="Mozilla/4.01 [en] (WinNT; U) [Netscape]"> +</HEAD> +<BODY> + +<CENTER> +<H1> +Change Log for the ldaputil library</H1></CENTER> + +<DT> +Author: Nitin More</DT> + +<DT> +E-mail: <A HREF="mailto:nitin@netscape.com">nitin@netscape.com</A></DT> + +<DT> +Phone: (415) 937-4240</DT> + +<CENTER></CENTER> + +<CENTER> +<HR WIDTH="100%"></CENTER> + +<CENTER></CENTER> + +<CENTER><B><FONT SIZE=+1>Changes since Apr 17, 1997</FONT></B></CENTER> + + +<P>Last Update: Aug 25, 1997 + +<P>All the new changes have been checked into the server3_tier_branch. +The server3_branch is frozen & contains the version of 'ldaputil' for +the SuiteSpot 3.0 release. +<H2> +Changed:</H2> +Several bug fixes went in since I last modified this file. The important +ones are: +<UL> +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=79373">79373</A>: Attributes +listed multiple times in certmap.conf were turining that attribute off +and enabling some other attribute. (For example, if you have "E" +as well as "MAIL" in FilterComps, they cancelled each other).</LI> + +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58474">58474</A>: If nested +group checks goes on for 30 recursions, a circular groups error is returned.</LI> + +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=80004">80004</A>: after +thoroughly testing the certmap utility, several (mostly minor) oddities +were found & fixed.</LI> +</UL> + +<H2> +Added:</H2> + +<UL> +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=79370">79370</A>: Group +membership check is optimized now through the new function <B>ldapu_auth_userdn_groupids</B>. +Use this new function to get the optimization. This function's algorithm +is as follows:</LI> + +<BR>The first search uses the following filter (w/o the group names!): +<OL><TT>(| (& (objectclass=groupofuniquenames)</TT> +<BR><TT> (uniquemember=<userDN>))</TT> +<BR><TT> (& (objectclass=groupofnames)(member=<userDN>)))</TT> +<BR> </OL> +This gives us all the groups the user is member of. We ask for only +the "CN" attributes of the returned groups. We check if "CN" of any +of the returned groups is one of the groups we have. If yes, we have +succeeded. If there are no groups returned then we have failed. +Otherwise, we continue with the nested group check. To perform that +check, we need DNs of all the groups the user is member of, which we already +have from the previous search. Now we repeat the search as follows: +<OL><TT>(| (& (objectclass=groupofuniquenames)</TT> +<BR><TT> (| (uniquemember=<grp1DN>)... +(uniquemember=<grpNDN>))</TT> +<BR><TT> (& (objectclass=groupofnames)</TT> +<BR><TT> (| (member=<grp1DN>)... (member=<grpNDN>))</TT></OL> +We check the list of groups returned by this search with the groups in +the ACL and recursively continue until we succeed or no more groups are +returned from the searches. + +<P>Advantages of this new function is it checks multiple groups at the +same time. Previously we were performing 2 ldap lookups per group. +Now we achieve this in a single ldap lookup! + +<P><B><BLINK>Caution</BLINK></B>: this function allows multiple groups +with the same "CN". + +<P>To use this function, you need to provide a list of group names in any +form (e.g comma separated string, a hash table, array of strings, etc.) +and a function to compare the name returned by the ldap lookup with your +group names.</UL> + +<CENTER><B><FONT SIZE=+1>Changes since Mar 22, 1997</FONT></B></CENTER> + + +<P>Last Update: Apr 17, 1997 + +<P>Now that all beta releases are out for servers using this library, I +could do some incompatible changes to make this library more flexible. +No more incompatible changes are planned (except for possibly one: see +http://scopus/bugsplat/show_bug.cgi?id=58482). <B><BLINK>All 3.0 SuiteSpot +servers supporting client auth need to upgrade to this version.</BLINK></B> +<H2> +Changed:</H2> + +<UL> +<LI> +<B>Exchanged certmap.h & ldaputil.h: </B>ldaputil.h had public API +but when the file was installed on the server root, it was called certmap.h. +Since we already had a certmap.h, this was causing lot of confusion. If +you were including "certmap.h", now include "ldaputil.h" and vice versa.</LI> + +<LI> +<B>Renamed 'SearchComps' to 'FilterComps'</B>: Shouldn't affect your code +but may affect tests and documentation.</LI> + +<LI> +<B>'VerifyCert' must be either "on" or "off"</B>: VerifyCert didn't have +a value. Now it must have a value. If it has a value of "on" then the "verification" +step is on, otherwise it is off.</LI> + +<LI> +<B>Important bug fixes</B>: One bug was causing stack corruption & +weird unpredictable results. The other important bug was to map 'E' in +cert to 'MAIL' in LDAP.</LI> +</UL> + +<H2> +Added:</H2> + +<UL> +<LI> +<B>Settable 'search function'</B>: User defined search function can be +set for cert to ldap mapping.</LI> + +<LI> +<B>ldapu_get_cert_ava_val & ldapu_free_cert_ava_val</B>: API functions +to get & free an attribute's value(s) from either the subject DN or +the issuer DN from a cert.</LI> +</UL> + +<H2> +Open Bugs:</H2> + +<UL>Following bugs may not get fixed by RTM.</UL> + +<UL> +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=51279">51279</A>: 'uniquemember' +bug</LI> + +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58474">58474</A>: +'circular groups' results in infinite loop</LI> + +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58478">58478</A>: Don't +allow a space as a valid delimeter for DNComps & FilterComps.</LI> + +<LI> +<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58482">58482</A>: Make +the 'search function' configurable.</LI> + +<BR> +<HR WIDTH="100%"></UL> + +<CENTER> +<H3> +Changes since Mar 18, 1997</H3></CENTER> + +<DT> +Last Update: Mar 22, 1997</DT> + +<DT> +A query on how to map a verisign certificate prompted these changes. +I was hoping I don't have to do any major changes when I wrote this document +on Mar 18. These are incompatible changes -- please review them before +you upgrade. I have checked in this file in CVS under "ns/netsite/lib/ldaputil/ldapu-changes.html". +I have added all the "XYZ_branch" and "XYZ_point" tags to this file so +that you can easily see this file in your tree. When I make significant +changes to this file/library, I will retag this file for your branch to +make it same as the server3_branch. [Let me know if I shouldn't do it for +your branch].</DT> + +<H2> +Changed:</H2> + +<UL> +<LI> +<B>ldapu_cert_to_ldap_entry</B>: The major change was to allow for the +mapped DN to be NULL and in that case, start the search from the basedn +of the LDAP server. This required API change so that the basedn can be +passed to the ldapu_cert_to_ldap_entry function. This change was required +for correctly mapping certs from verisign w/o writing plugins. The Verisign +certs can be mapped correctly using the following setting in the <ServerRoot>/userdb/certmap.conf +file:</LI> + +<UL><TT><FONT SIZE=+1>certmap verisign <verisign's DN></FONT></TT> +<BR><TT><FONT SIZE=+1>verisign:dncomps</FONT></TT> +<BR><TT><FONT SIZE=+1>verisign:searchcomps cn, e</FONT></TT></UL> +The mapped DN will be NULL so basedn will be used. The filter will +be +<BR>(& (cn="<user's CN>") (mail="<user's mail>")). The +new signature of ldapu_cert_to_ldap_entry is as follows: +<BR> int ldapu_cert_to_ldap_entry(void *cert, LDAP *ld, +const char *basedn, LDAPMessage **res); +<LI> +<B>verify cert functions</B>: A major change in how verify cert functions +work. This is function is now called even when multiple potential +matches are found for the cert. The mapping is successful if the +verify function can reduce the number of matches to exactly one. +For example, if there are multiple "Joe Smith" entries, at most one of +those will have the cert in it's "userCertificate" attr. The verify +function will select that entry. The verify function is called with +"LDAPMessage *res" containing all the potential matches. It should +return a pointer to the matched entry in the new "LDAPMessage **entry" +parameter. The new signature for CertVerifyFn_t is as follows:</LI> + +<BR> typedef int (*CertVerifyFn_t)(void *cert, LDAP *ld, +LDAPMessage *res, +<BR> + +<P> +LDAPMessage **entry); +<LI> +typedef int (*<B>CertMapInitiFn_t)</B>: Renamed from CertmapInitFn_t. +Now this has two extra parameters to make it easy to use it in a plugin. +Other API functions require "issuerDN" but this function was called with +"LDAPUCertMapInfo_t *certinfo". There was no public API function +to get the issuerDN from "certinfo". The new signature for CertMapInitFn_t +is as follows:</LI> + +<BR> typedef int (*CertMapInitFn_t)(void *certmap_info, +const char *issuerName, +<BR> +const char *issuerDN); +<LI> +(ldapauth.h) <B>ldapu_auth_* functions:</B> For multiple matches, these +functions now return LDAPU_ERR_MULTIPLE_MATCHES instead of LDAPU_FAILED. +This change was required to make nested group membership work efficiently +and enable the new functionality of verify cert function.</LI> +</UL> + +<DT> + +<HR WIDTH="100%"></DT> + +<CENTER> +<H3> +Changes since Feb 1, 1997</H3></CENTER> + +<DT> +Last Update: Mar 18, 1997</DT> + +<DT> +There have been several changes to the netsite/lib/ldaputil recently. If +you use this library, please start using the latest version of ldaputil +on the server3_branch so that all the servers go out with the same API +and behavior. Review the changes before you upgrade. If you don't plan +to upgrade, please let me know.</DT> + +<H2> +Renamed:</H2> +Following structures and functions are renamed. But this shouldn't affect +you unless you have written a certmap plugin. +<DD> +<TT><FONT SIZE=+1>struct CertMappingFunction_t ---> CertMapFn_t</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>struct CertVerifyFunction_t ---> CertVerifyFn_t</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_set_cert_mapping_function ---> ldapu_set_cert_mapfn</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_get_cert_mapping_function ---> ldapu_get_cert_mapfn</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_set_cert_verify_function ---> ldapu_set_cert_verifyfn</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_get_cert_verify_function ---> ldapu_get_cert_verifyfn</FONT></TT></DD> + +<H2> +Removed: (from .h)</H2> +Removed the following functions from .h files. They are still in the .c +file as static functions. I don't think these should be public. If you +think otherwise, let me know. +<DD> +<TT><FONT SIZE=+1>ldapu_cert_mapping_function_default</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_cert_verify_function_default</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_search_cert_dn</FONT></TT></DD> + +<DD> +<TT><FONT SIZE=+1>ldapu_subject_dn_to_ldap_dn</FONT></TT></DD> + +<H2> +Changed:</H2> +The following changes may affect you. Please review them carefully before +you upgrade to the latest version of ldaputil. +<UL> +<LI> +<TT>ldapu_auth_userdn_groupdn</TT> -- added <TT>const char *base</TT> argument +for group within group membership check</LI> + +<LI> +ldap_init and bind calls using <TT>LDAPDatabase_t *ldb</TT> retry once +if the LDAP server is/went down.</LI> + +<LI> +<TT>typedef CertVerifyFn_t</TT> has different arguments.</LI> + +<LI> +DNs from cert with escaped/quoted characters are correctly handled now.</LI> + +<LI> +cert to ldap entry mapping is optimized by not using string comparisons +during thruntime. A bitmask is created when the config file is read, cert +data is mapped to bits and compared against the bitmask.</LI> + +<LI> +Only the required attrs are retrieved in most <TT>ldap_search_s </TT>calls +from ldaputil. Some new functions were added to keep older functions the +same.</LI> + +<LI> +Fixed a core dump in ldapu_free_LDAPDatabase_t when using the local db.</LI> + +<LI> +ldaputil functions for initializing connection to the LDAP server and binding +to the server are thread-safe now. This requires linking to netsite/lib/base. +If you don't use libbase, you can turn off the thread-safe code using a +compile time option.</LI> +</UL> + +<H2> +Added:</H2> + +<UL> +<LI> +Documentation to functions in ldaputil.h (customer API) - ldaputil.h is +the external public API for customers to write there plugins.</LI> + +<LI> +<TT>ldapu_get_cert_der</TT> - returns raw DER encoded cert data</LI> + +<LI> +<TT>ldapu_cert_to_user</TT> - Similar to ldapu_cert_to_ldap_entry but only +retrieves the 'uid' and 'userCertificate' attributes from LDAP and also +extracts the user id.</LI> + +<LI> +<TT>ldapu_find_uid_attrs</TT> and <TT>ldapu_find_group_attrs</TT> - Similar +to ldapu_find_uid and ldapu_find_group resp., but only retrieves the specified +attributes. Internally used during password verification and group membership +checks.</LI> + +<LI> +<TT>ldapu_certinfo_delete, ldapu_certinfo_modify and ldapu_certinfo_save</TT> +- API for the certmap config file GUI tool which is not yet developed. +Any volunteers?</LI> +</UL> + +<H2> +<FONT SIZE=+1>TODO/Bugs:</FONT></H2> + +<UL> +<LI> +uniquemember attribute is not handled correctly in the group membership +check. If the user's entry has 'x500UniqueIdentifier' attribute populated, +the group could refer to the user entry by the user's dn followed by '#' +and an unique identifier. For example, the group entry could have:</LI> + +<DD> +uniquemember: cn=Joe Smith,o=Netscape,c=US#jsmith</DD> + +<DT> +where, 'jsmith' is one of the values of the 'x500UniqueIdentifier' attribute +for the Joe Smith entry.</DT> + +<DT> +</DT> +</UL> + +</BODY> +</HTML> diff --git a/lib/ldaputil/ldaputili.h b/lib/ldaputil/ldaputili.h new file mode 100644 index 00000000..fe8a65f3 --- /dev/null +++ b/lib/ldaputil/ldaputili.h @@ -0,0 +1,62 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _LDAPU_LDAPUTILI_H +#define _LDAPU_LDAPUTILI_H + +#include <ldaputil/ldaputil.h> + +#include <ssl.h> + +#define BIG_LINE 1024 + +extern const int SEC_OID_AVA_UNKNOWN; /* unknown OID */ + +#ifdef __cplusplus +extern "C" { +#endif + +SECStatus CERT_RFC1485_EscapeAndQuote (char *dst, int dstlen, char *src, int srclen); + +extern void* ldapu_list_empty (LDAPUList_t* list, LDAPUListNodeFn_t free_fn, void* arg); +extern void ldapu_list_move (LDAPUList_t* from, LDAPUList_t* into); + +extern int ldapu_get_cert_ava_val (void *cert_in, int which_dn, + const char *attr, char ***val_out); + +extern int ldapu_member_certificate_match (void* cert, const char* desc); + +/* Each of several LDAP API functions has a counterpart here. + * They behave the same, but their implementation may be replaced + * by calling ldapu_VTable_set(); as Directory Server does. + */ +#ifdef USE_LDAP_SSL +extern LDAP* ldapu_ssl_init( const char *host, int port, int encrypted ); +#else +extern LDAP* ldapu_init ( const char *host, int port ); +#endif +extern int ldapu_set_option( LDAP *ld, int opt, void *val ); +extern int ldapu_simple_bind_s( LDAP* ld, const char *username, const char *passwd ); +extern int ldapu_unbind( LDAP *ld ); +extern int ldapu_search_s( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, LDAPMessage **res ); +extern int ldapu_count_entries( LDAP *ld, LDAPMessage *chain ); +extern LDAPMessage* ldapu_first_entry( LDAP *ld, LDAPMessage *chain ); +extern LDAPMessage* ldapu_next_entry( LDAP *ld, LDAPMessage *entry ); +extern int ldapu_msgfree( LDAP *ld, LDAPMessage *chain ); +extern char* ldapu_get_dn( LDAP *ld, LDAPMessage *entry ); +extern void ldapu_memfree( LDAP *ld, void *dn ); +extern char* ldapu_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **ber ); +extern char* ldapu_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber ); +extern void ldapu_ber_free( LDAP *ld, BerElement *ber, int freebuf ); +extern char** ldapu_get_values( LDAP *ld, LDAPMessage *entry, const char *target ); +extern struct berval** ldapu_get_values_len( LDAP *ld, LDAPMessage *entry, const char *target ); +extern void ldapu_value_free( LDAP *ld, char **vals ); +extern void ldapu_value_free_len( LDAP *ld, struct berval **vals ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/ldaputil/utest/Makefile b/lib/ldaputil/utest/Makefile new file mode 100644 index 00000000..e6f2e5c6 --- /dev/null +++ b/lib/ldaputil/utest/Makefile @@ -0,0 +1,117 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Makefile for ldaputil unit test. +# +MCOM_ROOT=../../../.. +MODULE=LibLdapUtil + +OBJDEST=. +UTESTDEST=utest + +include ../../../nsconfig.mk + +MODULE_CFLAGS=-I$(NSROOT)/include + +include $(INCLUDE_DEPENDS) + +TESTFLAGS = -DUTEST -DDBG_PRINT -DDONT_USE_LDAP_SSL + +CC=CC +PURIFY= + +CSRC = stubs.c +CPPSRC = auth.cpp +TSRC = authtest +SRC = $(CSRC) $(CPPSRC) $(TSRC) +XSRC = ../ldapauth.c ../ldapdb.c ../errors.c ../dbconf.c ../certmap.c ../ldapauth.c ../init.c ../encode.c + +COBJ = $(CPPSRC:%.cpp=%.o) $(CSRC:%.c=%.o) +XOBJ = $(XSRC:../%.c=../utest/%.o) + +ifeq ($(ARCH), WINNT) + BINS=./auth.exe + LDAP_LIBLINK = $(addprefix $(LDAP_LIBPATH)/, $(addsuffix .lib, $(LDAP_LIBNAMES))) + XLIBS = ${LDAP_LIBLINK} + LOCAL_LINK_EXE = link -OUT:"$@" /MAP $(ARCH_LINK_DEBUG) $(LCFLAGS) /NOLOGO \ + /PDB:NONE /INCREMENTAL:NO /SUBSYSTEM:windows $(XLIBS) +else + BINS = auth + LDAP_LIBLINK = -L$(LDAP_LIBPATH) $(addprefix -l, ${LDAP_SOLIB_NAMES}) +endif + +ifeq ($(ARCH), SOLARIS) + XLIBS = -R$(LDAP_LIBPATH) ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC) -lthread -lposix4 -lsocket -lnsl -ldl +else + ifeq ($(ARCH), IRIX) + XLIBS = ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC) + else + ifeq ($(ARCH), WINNT) + echo "XLIBS = ${XLIBS}" + else + #Other UNIX platforms + XLIBS = -R$(LDAP_LIBPATH) ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC) -lthread -lposix4 -lsocket -lnsl -ldl + endif + endif +endif + +PLUGIN = plugin.so + +all: $(LIBLDAP) $(COBJ) $(TSRC) ${BINS} $(PLUGIN) + ./authtest 2> test.out + diff test.out test.ref + @echo + @echo "The unit test is passed if there is no diff output, and the" + @echo "Purify window shows no errors and 0 bytes leaked." + @echo + @echo "Run - gmake coverage - manually to get code coverage analysis." + @echo + +auth: $(XOBJ) $(COBJ) + $(PURIFY) $(CC) $(XLIBS) $^ -o $@ + +auth.exe: $(XOBJ) $(COBJ) + $(PURIFY) $(LOCAL_LINK_EXE) $(XOBJ) $(COBJ) ${XLIBS} + +testcert: testcert.o $(XOBJ) ../utest/cert.o + $(PURIFY) $(CC) $(XLIBS) $^ -o $@ + +%.o:%.c + $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) $< -o $@ + +../utest/%.o:../%.c + $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o + +../utest/%.o:../%.cpp + $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o + +PLUGIN_INC = ./include + +$(PLUGIN_INC): + mkdir -p include + +certmap.h: ../../../include/ldaputil/extcmap.h + \rm -rf $(PLUGIN_INC)/$@ + cp $^ $(PLUGIN_INC)/$@ + +ldap.h: $(LDAP_INCLUDE)/ldap.h + \rm -rf $(PLUGIN_INC)/$@ + cp $^ $(PLUGIN_INC)/$@ + +lber.h: $(LDAP_INCLUDE)/lber.h + \rm -rf $(PLUGIN_INC)/$@ + cp $^ $(PLUGIN_INC)/$@ + +example.o: example.c $(PLUGIN_INC) certmap.h ldap.h lber.h + $(CC) -c -I$(PLUGIN_INC) $*.c -o $(OBJDEST)/$*.o + +plugin.o: plugin.c $(PLUGIN_INC) certmap.h ldap.h lber.h + $(PURIFY) $(CC) -c -I. -I$(PLUGIN_INC) $*.c -o $(OBJDEST)/$*.o + +$(PLUGIN): plugin.o + $(LINK_DLL) $^ diff --git a/lib/ldaputil/utest/auth.cpp b/lib/ldaputil/utest/auth.cpp new file mode 100644 index 00000000..e952f742 --- /dev/null +++ b/lib/ldaputil/utest/auth.cpp @@ -0,0 +1,574 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include <prinit.h> // for PR_Init +#include <prpriv.h> // for PR_Exit +#include <ldaputil/certmap.h> +#include <ldaputil/init.h> +#include <ldaputil/ldapdb.h> +#include <ldaputil/ldapauth.h> +#include <ldaputil/dbconf.h> +#include <ldaputil/ldaputil.h> +#include <ldap.h> + +static const char* dllname = "plugin.so"; + +char *global_issuer_dn = "o=Netscape Communications, c=US"; + +#define NSPR_INIT(Program) (PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 8)) + +static int ldapu_certinfo_save_test (const char *fname, const char *old_fname) +{ + int rv; + + /* Read the original certmap config file first */ + rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp"); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n", + ldapu_err2string(rv)); + } + + return rv; +} + +static int ldapu_certinfo_delete_test (const char *fname, const char *old_fname) +{ + int rv; + + /* Read the original certmap config file first */ + rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + /* rv = ldapu_certinfo_delete("o=Ace Industry, c=US"); */ + rv = ldapu_certinfo_delete("o=Netscape Communications, c=US"); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp"); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n", + ldapu_err2string(rv)); + } + + return rv; +} + +static int ldapu_certinfo_new_test (const char *fname, const char *old_fname) +{ + int rv; + LDAPUPropValList_t *propval_list; + LDAPUPropVal_t *propval; + + /* Read the original certmap config file first */ + rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + /* Setup propval_list */ + rv = ldapu_list_alloc(&propval_list); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_propval_alloc("prop1", "val1", &propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_list_add_info(propval_list, propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_propval_alloc("prop2", "val2", &propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_list_add_info(propval_list, propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_propval_alloc("prop3", 0, &propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_list_add_info(propval_list, propval); + if (rv != LDAPU_SUCCESS) return rv; + + rv = ldapu_certinfo_modify("newmap", "o=Mcom Communications, c=US", + propval_list); + + ldapu_propval_list_free(propval_list); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp"); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n", + ldapu_err2string(rv)); + } + + return rv; +} + +static int get_dbnames_test (const char *mapfile) +{ + char **names; + int cnt; + int rv; + int i; + + rv = dbconf_get_dbnames(mapfile, &names, &cnt); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "get_dbnames_test failed. Reason: %s\n", + ldapu_err2string(rv)); + } + else { + for(i = 0; i < cnt; i++) { + fprintf(stderr, "\tdbname[%d] = \"%s\"\n", + i, names[i]); + } + } + + dbconf_free_dbnames(names); + + return rv; +} + +static int case_ignore_strcmp (const char *s1, const char *s2) +{ + int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */ + + if (!s1) return !s2 ? 0 : 0-tolower(*s2); + else if (!s2) return tolower(*s1); + + while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; } + + if (!*s1) + return *s2 ? 0-tolower(*s2) : 0; + else if (!*s2) + return tolower(*s1); + else + return ls1 - ls2; +} + +#define STRCASECMP3(s1, s2, rv) \ +{ \ + int i = case_ignore_strcmp(s1, s2); \ + fprintf(stderr, "strcasecmp(\"%s\", \"%s\")\t=\t%d\t%s\tExpected: %d\n", \ + s1 ? s1 : "<NULL>", s2 ? s2 : "<NULL>", \ + i, i == rv ? "SUCCESS" : "FAILED", rv); \ +} + +#ifndef XP_WIN32 +#define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, strcasecmp(s1, s2)) +#else +#define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, case_ignore_strcmp(s1, s2)) +#endif + +static void strcasecmp_test () +{ + STRCASECMP3(0, "aBcD", 0-tolower('a')); + STRCASECMP3(0, 0, 0); + STRCASECMP3("aBcD", 0, tolower('a')); + + STRCASECMP("AbCd", "aBcD"); + STRCASECMP("AbCd", "abcd"); + STRCASECMP("ABCD", "ABCD"); + STRCASECMP("abcd", "abcd"); + + STRCASECMP("AbCd", "aBcD3"); + STRCASECMP("AbCd", "abcd3"); + STRCASECMP("ABCD", "ABCD3"); + STRCASECMP("abcd", "abcd3"); + + STRCASECMP("AbCd1", "aBcD"); + STRCASECMP("AbCd2", "abcd"); + STRCASECMP("ABCDX", "ABCD"); + STRCASECMP("abcdY", "abcd"); + + STRCASECMP("AbCd5", "aBcD1"); + STRCASECMP("AbCd5", "abcd1"); + STRCASECMP("ABCD5", "ABCD1"); + STRCASECMP("abcd5", "abcd1"); + + STRCASECMP("AbCd2", "aBcDp"); + STRCASECMP("AbCd2", "abcdQ"); + STRCASECMP("ABCD2", "ABCDr"); + STRCASECMP("abcd2", "abcdS"); +} + +static int certmap_tests (const char *config_file) { return 0; } + +static int read_config_test (const char *config_file, const char *dbname, + const char *url, + const char *binddn, const char *bindpw) +{ + int rv; + DBConfDBInfo_t *db_info; + char *dn; + char *pw; + + rv = dbconf_read_default_dbinfo(config_file, &db_info); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "config_test failed: %s\n", + ldapu_err2string(rv)); + return LDAPU_FAILED; + } + + if (strcmp(db_info->dbname, dbname) || + strcmp(db_info->url, url)) { + fprintf(stderr, "config_test failed: %s\n", + "first line in config file is wrong"); + return LDAPU_FAILED; + } + + if ((ldapu_dbinfo_attrval(db_info, "binddn", &dn) != LDAPU_SUCCESS) || + (ldapu_dbinfo_attrval(db_info, "bindpw", &pw) != LDAPU_SUCCESS)) + { + fprintf(stderr, "config_test failed: %s\n", + "properties are missing"); + return LDAPU_FAILED; + } + + if (strcmp(dn, binddn) || + strcmp(pw, bindpw)) { + fprintf(stderr, "config_test failed: %s\n", + "property values are wrong"); + return LDAPU_FAILED; + } + + fprintf(stderr, "binddn from config file: \"%s\"\n", dn); + fprintf(stderr, "bindpw from config file: \"%s\"\n", pw); + + /* cleanup */ + dbconf_free_dbinfo(db_info); + free(dn); + free(pw); + + return LDAPU_SUCCESS; +} + +static int config_test (const char *binddn, const char *bindpw) +{ + char *config_file = "config_out.conf"; + FILE *fp = fopen(config_file, "w"); + const char *dbname = "default"; + const char *url = "file:/foobar/path"; + int rv; + + if (!fp) return LDAPU_FAILED; + + dbconf_output_db_directive(fp, dbname, url); + dbconf_output_propval(fp, dbname, "binddn", binddn, 0); + dbconf_output_propval(fp, dbname, "bindpw", bindpw, 1); + + fclose(fp); + + fprintf(stderr, "Config file written: %s\n", config_file); + + rv = read_config_test(config_file, dbname, url, binddn, bindpw); + + return rv; +} + +static int +compare_groupid(const void *arg, const char *group, const int len) +{ + auto const char* groupid = (const char*)arg; + auto int err = LDAPU_FAILED; + if (len == strlen (groupid) && !strncasecmp (groupid, group, len)) { + err = LDAPU_SUCCESS; + } + return err; +} + +static int +compare_group(LDAP* directory, LDAPMessage* entry, void* set) +{ + auto int err = LDAPU_FAILED; + auto char** vals = ldap_get_values (directory, entry, "CN"); + if (vals) { + auto char** val; + for (val = vals; *val; ++val) { + if (!strcasecmp (*val, (char*)set)) { + err = LDAPU_SUCCESS; + break; + } + } + ldap_value_free (vals); + } + return err; +} + +int perform_test (int argc, char *argv[]) +{ + int test_type; + int retval = LDAPU_SUCCESS; + DBConfDBInfo_t *db_info; + LDAPDatabase_t *ldb; + LDAP *ld; + char *dbmap_file = "dblist.conf"; + char *binddn = 0; + char *bindpw = 0; + char *basedn; + int retry = 1; + int rv; + + fprintf(stderr, "\nStart of test: ./auth %s \"%s\" \"%s\"\n", + argv[1], argv[2], argv[3]); + + rv = dbconf_read_default_dbinfo(dbmap_file, &db_info); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "Error reading dbmap file \"%s\". Reason: %s\n", + dbmap_file, ldapu_err2string(rv)); + return rv; + } + + ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDDN, &binddn); + ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDPW, &bindpw); + + rv = ldapu_url_parse (db_info->url, binddn, bindpw, &ldb); + free(binddn); + free(bindpw); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "Error parsing ldap url \"%s\". Reason: %s\n", + db_info->url, ldapu_err2string(rv)); + return rv; + } + + basedn = ldb->basedn; + + test_type = atoi(argv[1]); + + retry = 1; + + while(retry) { + retry = 0; + + rv = ldapu_ldap_init_and_bind (ldb); + + if (rv != LDAPU_SUCCESS) { + fprintf(stderr, "Error initializing connection to LDAP. Reason: %s\n", + ldapu_err2string(rv)); + return rv; + } + + ld = ldb->ld; + + switch(test_type) { + case 1: + fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupdn:\t\"%s\"\n", + argv[2], argv[3]); + retval = ldapu_auth_userdn_groupdn(ld, argv[2], argv[3], basedn); + break; + + case 2: + fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupdn:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_uid_groupdn(ld, argv[2], argv[3], basedn); + break; + + case 3: + fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_uid_groupid(ld, argv[2], argv[3], basedn); + break; + + case 4: + fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_userdn_groupid(ld, argv[2], argv[3], basedn); + break; + + case 5: + fprintf(stderr, "\nuserdn:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_userdn_attrfilter(ld, argv[2], argv[3]); + break; + + case 6: + fprintf(stderr, "\nuid:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_uid_attrfilter(ld, argv[2], argv[3], basedn); + break; + + case 7: + fprintf(stderr, "\nuserdn:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_userdn_password(ld, argv[2], argv[3]); + break; + + case 8: + fprintf(stderr, "\nuid:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_uid_password(ld, argv[2], argv[3], basedn); + break; + + case 9: { + /* plugin test */ + LDAPMessage *entry = 0; + LDAPMessage *res = 0; + + fprintf(stderr, "Cert Map issuer DN: \"%s\"\n", argv[2]); + fprintf(stderr, "Cert Map subject DN: \"%s\"\n", argv[3]); + retval = ldaputil_init("certmap.conf", dllname, NULL, NULL, NULL); + + if (retval != LDAPU_SUCCESS) { + fprintf(stderr, "Cert Map info test failed. Reason: %s\n", + ldapu_err2string(retval)); + break; + } + + if (*(argv[2])) + global_issuer_dn = argv[2]; + else + global_issuer_dn = 0; + + retval = ldapu_cert_to_ldap_entry(argv[3], ld, ldb->basedn, &res); + + if (retval == LDAPU_SUCCESS) { + char *dn; + + entry = ldap_first_entry(ld, res); + dn = ldap_get_dn(ld, entry); + fprintf(stderr, "Matched entry to cert: \"%s\"\n", dn); + ldap_memfree(dn); + } + else if (retval == LDAPU_FAILED) { + /* Not an error but couldn't map the cert */ + } + else { + fprintf(stderr, "Cert Map info test failed. Reason: %s\n", + ldapu_err2string(retval)); + break; + } + + /* TEMPORARY -- when & how to free the entry */ + if (res) ldap_msgfree(res); + + break; + } /* case 9 */ + + case 10: + if ((retval = config_test(argv[2], argv[3])) == LDAPU_SUCCESS) { + fprintf(stderr, "Config file test succeeded\n"); + } + else { + fprintf(stderr, "Config file test failed\n"); + } + break; + + case 11: + retval = get_dbnames_test(argv[2]); + break; + + case 12: + retval = ldapu_certinfo_save_test(argv[2], argv[3]); + break; + + case 13: + retval = ldapu_certinfo_delete_test(argv[2], argv[3]); + break; + + case 14: + retval = ldapu_certinfo_new_test(argv[2], argv[3]); + break; + + case 15: + fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]); + { + auto LDAPU_DNList_t* userDNs = ldapu_DNList_alloc(); + ldapu_DNList_add(userDNs, argv[2]); + retval = ldapu_auth_usercert_groups(ld, basedn, userDNs, NULL, + argv[3], compare_group, 30, NULL); + ldapu_DNList_free(userDNs); + } + break; + + case 16: + fprintf(stderr, "\nuserCert:\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]); + retval = ldapu_auth_usercert_groupids(ld, NULL/*userDN*/, argv[2], argv[3], + compare_groupid, basedn, NULL/*group_out*/); + break; + + } /* switch */ + + if (retval == LDAP_SERVER_DOWN) { + /* retry */ + retry = 1; + ldb->ld = 0; + } + else if (retval == LDAPU_SUCCESS) { + fprintf(stderr, "Authentication succeeded.\n"); + } + else { + fprintf(stderr, "Authentication failed.\n"); + } + } + + /* cleanup */ +// ldapu_free_LDAPDatabase_t(ldb); +// dbconf_free_dbinfo(db_info); +// ldaputil_exit(); + return retval; +} + +int main (int argc, char *argv[]) +{ + int rv; + + NSPR_INIT("auth"); + + if (argc != 4) { + fprintf(stderr, "argc = %d\n", argc); + fprintf(stderr, "usage: %s test_type user_dn group_dn\n", argv[0]); + fprintf(stderr, "\t%s 1 <userdn> <groupdn>\n", argv[0]); + fprintf(stderr, "\t%s 2 <uid> <groupdn>\n", argv[0]); + fprintf(stderr, "\t%s 3 <uid> <groupid>\n", argv[0]); + fprintf(stderr, "\t%s 4 <userdn> <groupid>\n", argv[0]); + fprintf(stderr, "\t%s 5 <userdn> <attrFilter>\n", argv[0]); + fprintf(stderr, "\t%s 6 <uid> <attrFilter>\n", argv[0]); + fprintf(stderr, "\t%s 7 <userdn> <password>\n", argv[0]); + fprintf(stderr, "\t%s 8 <uid> <password>\n", argv[0]); + fprintf(stderr, "\t%s 9 <certmap.conf> <subjectDN>\n", argv[0]); + fprintf(stderr, "\t%s 10 <binddn> <bindpw>\n", argv[0]); + fprintf(stderr, "\t%s 11 <dbmap> <ignore>\n", argv[0]); + fprintf(stderr, "\t%s 12 <newconfig> <oldconfig> ... to test save\n", argv[0]); + fprintf(stderr, "\t%s 13 <newconfig> <oldconfig> ... to test delete\n", argv[0]); + fprintf(stderr, "\t%s 14 <newconfig> <oldconfig> ... to test add\n", argv[0]); + fprintf(stderr, "\t%s 15 <userdn> <groupid>\n", argv[0]); + fprintf(stderr, "\t%s 16 <userCertDescription> <groupid>\n", argv[0]); + exit(LDAP_PARAM_ERROR); + } + + rv = perform_test(argc, argv); + /* PR_Exit(); */ + + return rv; +} + diff --git a/lib/ldaputil/utest/authtest b/lib/ldaputil/utest/authtest new file mode 100755 index 00000000..c713349b --- /dev/null +++ b/lib/ldaputil/utest/authtest @@ -0,0 +1,106 @@ +#!/bin/ksh +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# setup for test +USERDN="cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +USER2DN="cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + +UID="hmiller" +U2ID="scarter" + +GROUPDN="cn=Directory Administrators, o=Ace Industry, c=US" +GROUPID="Directory Administrators" + +ATTRFILTER="mail=hmiller@aceindustry.com" +ATTR2FILTER="mail=scarter@aceindustry.com" + +function check_result { + echo "\nStart of test: $1 $2 \"$3\" \"$4\"" + if ( `$1 $2 "$3" "$4"` ) then + if [ $5 == "fail" ]; then + echo "**** Test Failed ****"; + else + echo "Test Succeeded"; + fi + else + if [ $5 == "fail" ]; then + echo "Test Succeeded"; + else + echo "**** Test Failed ****"; + fi + fi +} + +function must_fail { + check_result $1 $2 "$3" "$4" "fail"; +} + +function must_succeed { + check_result $1 $2 "$3" "$4" "success"; +} + +# test for <userdn> <groupdn> +must_succeed ./auth 1 "${USERDN}" "${GROUPDN}" +must_fail ./auth 1 "${USER2DN}" "${GROUPDN}" + +# test for <uid> <groupdn> +must_succeed ./auth 2 "${UID}" "${GROUPDN}" +must_fail ./auth 2 "${U2ID}" "${GROUPDN}" + +# test for <uid> <groupid> +must_succeed ./auth 3 "${UID}" "${GROUPID}" +must_fail ./auth 3 "${U2ID}" "${GROUPID}" + +# test for <userdn> <groupid> +must_succeed ./auth 4 "${USERDN}" "${GROUPID}" +must_fail ./auth 4 "${USER2DN}" "${GROUPID}" +must_succeed ./auth 15 "${USERDN}" "${GROUPID}" +must_fail ./auth 15 "${USER2DN}" "${GROUPID}" +must_succeed ./auth 16 "{${USERDN}" "${GROUPID}" +must_fail ./auth 16 "{${USER2DN}" "${GROUPID}" + +# test for <userdn> <attrFilter> +must_succeed ./auth 5 "${USERDN}" "${ATTRFILTER}" +must_fail ./auth 5 "${USERDN}" "${ATTR2FILTER}" +must_fail ./auth 5 "${USER2DN}" "${ATTRFILTER}" + +# test for <uid> <attrFilter> +must_succeed ./auth 6 "${UID}" "${ATTRFILTER}" +must_fail ./auth 6 "${UID}" "${ATTR2FILTER}" +must_fail ./auth 6 "${U2ID}" "${ATTRFILTER}" + +# test for <userdn> <password> +must_succeed ./auth 7 "${USERDN}" "hillock" +must_fail ./auth 7 "${USERDN}" "garbage" + +# test for <uid> <password> +must_succeed ./auth 8 "${UID}" "hillock" +must_fail ./auth 8 "${UID}" "garbage" + +#test for cert to ldap entry mapping +must_succeed ./auth 9 "o=Ace Industry, c=US" "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" +#must_fail ./auth 9 "default" "cn=Kirsten Vaughan, o=Ace Industry, c=US" + +# test for encode/decode bindpw +must_succeed ./auth 10 "cn=Foo Bar, o=Netscape Communication, c=US" "foobar" + +# test for reading dbnames from dbswitch.conf file +must_succeed ./auth 11 dblist.conf ignore + +# test for saving certmap info +must_succeed ./auth 12 certmap.new certmap.conf +cat certmap.conf certmap.new 1>&2 + +# test for delete certmap info +must_succeed ./auth 13 certmap.new certmap.conf +cat certmap.conf certmap.new 1>&2 + +# test for add certmap info +must_succeed ./auth 14 certmap.new certmap.conf +cat certmap.conf certmap.new 1>&2 + diff --git a/lib/ldaputil/utest/certmap.conf b/lib/ldaputil/utest/certmap.conf new file mode 100644 index 00000000..53ab6fca --- /dev/null +++ b/lib/ldaputil/utest/certmap.conf @@ -0,0 +1,36 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default +#default:DNComps o, ou ,c +#default:FilterComps cn + + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l +#default1:verifycert + +# Following line has trailing spaces +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on diff --git a/lib/ldaputil/utest/dblist.conf b/lib/ldaputil/utest/dblist.conf new file mode 100644 index 00000000..c38580ac --- /dev/null +++ b/lib/ldaputil/utest/dblist.conf @@ -0,0 +1,15 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +directory default ldap://:3334/o=Airius.com +directory default1 ldap:///o=Ace Industry, c=US +directory default2 ldap:///o=Ace Industry, c=US +directory default3 ldap:///o=Ace Industry, c=US +directory default4 ldap:///o=Ace Industry, c=US +directory default5 ldap:///o=Ace Industry, c=US +directory default6 ldap:///o=Ace Industry, c=US diff --git a/lib/ldaputil/utest/example.c b/lib/ldaputil/utest/example.c new file mode 100644 index 00000000..185fbe51 --- /dev/null +++ b/lib/ldaputil/utest/example.c @@ -0,0 +1,116 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> + +#include <certmap.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* The init function must be defined extern "C" if using a C++ compiler */ +int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN); + +#ifdef __cplusplus +} +#endif + + +static int extract_ldapdn_and_filter (const char *subjdn, void *certmap_info, + char **ldapDN, char **filter) +{ + /* extract the ldapDN and filter from subjdn */ + /* You can also use the ldapu_certmap_info_attrval function to get value + of a config file parameter for the certmap_info. */ + return LDAPU_SUCCESS; +} + +static int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info, + char **ldapDN, char **filter) +{ + char *subjdn; + int rv; + + fprintf(stderr, "plugin_mapping_fn called.\n"); + rv = ldapu_get_cert_subject_dn(cert, &subjdn); + + if (rv != LDAPU_SUCCESS) return rv; + + *ldapDN = 0; + *filter = 0; + + rv = extract_ldapdn_and_filter(subjdn, certmap_info, ldapDN, filter); + + if (rv != LDAPU_SUCCESS) { + /* This function must return LDAPU_FAILED or + LDAPU_CERT_MAP_FUNCTION_FAILED on error */ + return LDAPU_CERT_MAP_FUNCTION_FAILED; + } + + return LDAPU_SUCCESS; +} + +static int plugin_cmp_certs (void *subject_cert, + void *entry_cert_binary, + unsigned long entry_cert_len) +{ + /* compare the certs */ + return LDAPU_SUCCESS; +} + +static int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info, + LDAPMessage *res, LDAPMessage **entry_out) +{ + LDAPMessage *entry; + struct berval **bvals; + char *cert_attr = "userCertificate;binary"; + int i; + int rv; + + fprintf(stderr, "plugin_verify_fn called.\n"); + *entry_out = 0; + + for (entry = ldap_first_entry(ld, res); entry != NULL; + entry = ldap_next_entry(ld, entry)) + { + if ((bvals = ldap_get_values_len(ld, entry, cert_attr)) == NULL) { + rv = LDAPU_CERT_VERIFY_FUNCTION_FAILED; + /* Maybe one of the remaining entries will match */ + continue; + } + + for ( i = 0; bvals[i] != NULL; i++ ) { + rv = plugin_cmp_certs (cert, + bvals[i]->bv_val, + bvals[i]->bv_len); + + if (rv == LDAPU_SUCCESS) { + break; + } + } + + ldap_value_free_len(bvals); + + if (rv == LDAPU_SUCCESS) { + *entry_out = entry; + break; + } + } + + return rv; +} + +int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN) +{ + fprintf(stderr, "plugin_init_fn called.\n"); + ldapu_set_cert_mapfn(issuerDN, plugin_mapping_fn); + ldapu_set_cert_verifyfn(issuerDN, plugin_verify_fn); + return LDAPU_SUCCESS; +} + diff --git a/lib/ldaputil/utest/plugin.c b/lib/ldaputil/utest/plugin.c new file mode 100644 index 00000000..29f7765b --- /dev/null +++ b/lib/ldaputil/utest/plugin.c @@ -0,0 +1,115 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include <plugin.h> /* must define extern "C" functions */ +#include <certmap.h> /* Public Certmap API */ + +static CertSearchFn_t default_searchfn = 0; + +static int plugin_attr_val (void *cert, int which_dn, const char *attr) +{ + char **val; + int rv = ldapu_get_cert_ava_val(cert, which_dn, attr, &val); + char **attr_val = val; /* preserve the pointer for free */ + + if (rv != LDAPU_SUCCESS || !val) { + fprintf(stderr, "\t%s: *** Failed ***\n", attr); + } + else if (!*val) { + fprintf(stderr, "\t%s: *** Empty ***\n", attr); + } + else { + fprintf(stderr, "\t%s: \"%s\"", attr, *val++); + while(*val) { + fprintf(stderr, ", \"%s\"", *val++); + } + fprintf(stderr, "\n"); + } + + ldapu_free_cert_ava_val(attr_val); + + return LDAPU_SUCCESS; +} + +static int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info, + char **ldapDN, char **filter) +{ + char *subjdn; + char *issuerDN; + char *ptr; + char *comma; + + fprintf(stderr, "plugin_mapping_fn called.\n"); + ldapu_get_cert_subject_dn(cert, &subjdn); + ldapu_get_cert_issuer_dn(cert, &issuerDN); + + fprintf(stderr, "Value of attrs from subject DN & issuer DN:\n"); + fprintf(stderr, "\tCert: \"%s\"\n", (char *)cert); + fprintf(stderr, "\tsubjdn: \"%s\"\n", subjdn); + plugin_attr_val(cert, LDAPU_SUBJECT_DN, "cn"); + plugin_attr_val(cert, LDAPU_SUBJECT_DN, "ou"); + plugin_attr_val(cert, LDAPU_SUBJECT_DN, "o"); + plugin_attr_val(cert, LDAPU_SUBJECT_DN, "c"); + fprintf(stderr, "\tissuerDN: \"%s\"\n", issuerDN); + plugin_attr_val(cert, LDAPU_ISSUER_DN, "cn"); + plugin_attr_val(cert, LDAPU_ISSUER_DN, "ou"); + plugin_attr_val(cert, LDAPU_ISSUER_DN, "o"); + plugin_attr_val(cert, LDAPU_ISSUER_DN, "c"); + + if (subjdn && *subjdn) { + comma = ptr = strchr(subjdn, ','); + + while(*ptr == ',' || isspace(*ptr)) ptr++; + *ldapDN = strdup(ptr); + + /* Set filter to the first AVA in the subjdn */ + *filter = subjdn; + *comma = 0; + } + else { + *ldapDN = 0; + *filter = 0; + } + + return LDAPU_SUCCESS; +} + +static int plugin_search_fn (void *cert, LDAP *ld, void *certmap_info, + const char *basedn, + const char *dn, const char *filter, + const char **attrs, LDAPMessage **res) +{ + fprintf(stderr, "plugin_search_fn called.\n"); + return (*default_searchfn)(cert, ld, certmap_info, basedn, dn, filter, + attrs, res); +} + +static int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info, + LDAPMessage *res, LDAPMessage **entry) +{ + fprintf(stderr, "plugin_verify_fn called.\n"); + *entry = ldap_first_entry(ld, res); + return LDAPU_SUCCESS; +} + +int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN) +{ + fprintf(stderr, "plugin_init_fn called.\n"); + ldapu_set_cert_mapfn(issuerDN, plugin_mapping_fn); + ldapu_set_cert_verifyfn(issuerDN, plugin_verify_fn); + + if (!default_searchfn) + default_searchfn = ldapu_get_cert_searchfn(issuerDN); + + ldapu_set_cert_searchfn(issuerDN, plugin_search_fn); + return LDAPU_SUCCESS; +} + diff --git a/lib/ldaputil/utest/plugin.h b/lib/ldaputil/utest/plugin.h new file mode 100644 index 00000000..124a121a --- /dev/null +++ b/lib/ldaputil/utest/plugin.h @@ -0,0 +1,20 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _CERTMAP_PLUGIN_H +#define _CERTMAP_PLUGIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int plugin_init_fn (void *certmap_info, const char *issuerName, + const char *issuerDN); + +#ifdef __cplusplus +} +#endif + +#endif /* _CERTMAP_PLUGIN_H */ diff --git a/lib/ldaputil/utest/stubs.c b/lib/ldaputil/utest/stubs.c new file mode 100644 index 00000000..03717331 --- /dev/null +++ b/lib/ldaputil/utest/stubs.c @@ -0,0 +1,107 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <ctype.h> /* isspace */ +#include <string.h> +#include <stdio.h> /* sprintf */ +#include <stdlib.h> /* malloc */ + +#include <ldap.h> +#include <ldaputil/certmap.h> +#include <ldaputil/cert.h> +#include <ldaputil/errors.h> + +#define BIG_LINE 1024 + +NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN) +{ + char *cert = (char *)cert_in; + + *subjectDN = strdup((char *)cert); + return *subjectDN ? LDAPU_SUCCESS : LDAPU_FAILED; +} + +NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert, char **issuerDN) +{ + extern char *global_issuer_dn; + /* TEMPORARY -- not implemented yet*/ + *issuerDN = global_issuer_dn ? strdup(global_issuer_dn) : 0; + return LDAPU_SUCCESS; +} + +/* A stub to remove link errors -- ignore SSL */ +LDAP *ldapssl_init (const char *host, int port, int secure) +{ + LDAP *ld = 0; + + if ((ld = ldap_init(host, port)) == NULL) { + fprintf(stderr, "ldap_init: Failed to initialize connection"); + return(0); + } + + return ld; +} + +NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn, + const char *attr, char ***val_out) +{ + int rv; + char *cert_dn; + char **ptr; + char **val; + char *dnptr; + char attr_eq1[BIG_LINE]; + char attr_eq2[BIG_LINE]; + char *comma; + + *val_out = 0; + + if (which_dn == LDAPU_SUBJECT_DN) + rv = ldapu_get_cert_subject_dn(cert_in, &cert_dn); + else if (which_dn == LDAPU_ISSUER_DN) + rv = ldapu_get_cert_issuer_dn(cert_in, &cert_dn); + else + return LDAPU_ERR_INVALID_ARGUMENT; + + if (rv != LDAPU_SUCCESS) return rv; + + val = (char **)malloc(32*sizeof(char *)); + + if (!val) return LDAPU_ERR_OUT_OF_MEMORY; + + ptr = val; + sprintf(attr_eq1, "%s =", attr); + sprintf(attr_eq2, "%s=", attr); + + while(cert_dn && + ((dnptr = strstr(cert_dn, attr_eq1)) || + (dnptr = strstr(cert_dn, attr_eq2)))) + { + dnptr = strchr(dnptr, '='); + dnptr++; + while(isspace(*dnptr)) dnptr++; + comma = strchr(dnptr, ','); + + if (comma) { + *ptr = (char *)malloc((comma-dnptr+1)*sizeof(char)); + strncpy(*ptr, dnptr, (comma-dnptr)); + (*ptr++)[comma-dnptr] = 0; + } + else { + *ptr++ = strdup(dnptr); + } + cert_dn = comma; + } + + *ptr = 0; + *val_out = val; + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der, + unsigned int *len) +{ + return LDAPU_FAILED; +} diff --git a/lib/ldaputil/utest/stubs.cpp b/lib/ldaputil/utest/stubs.cpp new file mode 100644 index 00000000..92e6f978 --- /dev/null +++ b/lib/ldaputil/utest/stubs.cpp @@ -0,0 +1,102 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <ctype.h> /* isspace */ +#include <string.h> +#include <stdio.h> /* sprintf */ +#include <stdlib.h> /* malloc */ + +#include <ldaputil/ldaputil.h> +#include <ldaputil/cert.h> +#include <ldaputil/errors.h> +#include "../ldaputili.h" + +#define BIG_LINE 1024 + +NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN) +{ + char *cert = (char *)cert_in; + + *subjectDN = strdup((char *)cert); + return *subjectDN ? LDAPU_SUCCESS : LDAPU_FAILED; +} + +NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert, char **issuerDN) +{ + /* TEMPORARY -- not implemented yet*/ + *issuerDN = strdup("o=Netscape Communications, c=US"); + return *issuerDN ? LDAPU_SUCCESS : LDAPU_FAILED; +} + +NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn, + const char *attr, char ***val_out) +{ + int rv; + char *cert_dn; + char **ptr; + char **val; + char *dnptr; + char attr_eq1[BIG_LINE]; + char attr_eq2[BIG_LINE]; + char *comma; + + *val_out = 0; + + if (which_dn == LDAPU_SUBJECT_DN) + rv = ldapu_get_cert_subject_dn(cert_in, &cert_dn); + else if (which_dn == LDAPU_ISSUER_DN) + rv = ldapu_get_cert_issuer_dn(cert_in, &cert_dn); + else + return LDAPU_ERR_INVALID_ARGUMENT; + + if (rv != LDAPU_SUCCESS) return rv; + + val = (char **)malloc(32*sizeof(char *)); + + if (!val) return LDAPU_ERR_OUT_OF_MEMORY; + + ptr = val; + sprintf(attr_eq1, "%s =", attr); + sprintf(attr_eq2, "%s=", attr); + + while(cert_dn && + ((dnptr = strstr(cert_dn, attr_eq1)) || + (dnptr = strstr(cert_dn, attr_eq2)))) + { + dnptr = strchr(dnptr, '='); + dnptr++; + while(isspace(*dnptr)) dnptr++; + comma = strchr(dnptr, ','); + + if (comma) { + *ptr = (char *)malloc((comma-dnptr+1)*sizeof(char)); + strncpy(*ptr, dnptr, (comma-dnptr)); + (*ptr++)[comma-dnptr] = 0; + } + else { + *ptr++ = strdup(dnptr); + } + cert_dn = comma; + } + + *ptr = 0; + *val_out = val; + return LDAPU_SUCCESS; +} + +NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der, + unsigned int *len) +{ + return LDAPU_FAILED; +} + +int +ldapu_member_certificate_match (void* cert, const char* desc) +{ + if (!strcasecmp ((char*)cert, desc)) { + return LDAPU_SUCCESS; + } + return LDAPU_FAILED; +} diff --git a/lib/ldaputil/utest/test.ref b/lib/ldaputil/utest/test.ref new file mode 100644 index 00000000..fae39056 --- /dev/null +++ b/lib/ldaputil/utest/test.ref @@ -0,0 +1,448 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +Start of test: ./auth 1 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "cn=Directory Administrators, o=Ace Industry, c=US" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +groupdn: "cn=Directory Administrators, o=Ace Industry, c=US" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +Authentication succeeded. + +Start of test: ./auth 1 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "cn=Directory Administrators, o=Ace Industry, c=US" + +userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" +groupdn: "cn=Directory Administrators, o=Ace Industry, c=US" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 2 "hmiller" "cn=Directory Administrators, o=Ace Industry, c=US" + +uid: "hmiller" +groupdn: "cn=Directory Administrators, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "uid=hmiller" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +Authentication succeeded. + +Start of test: ./auth 2 "scarter" "cn=Directory Administrators, o=Ace Industry, c=US" + +uid: "scarter" +groupdn: "cn=Directory Administrators, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "uid=scarter" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 3 "hmiller" "Directory Administrators" + +uid: "hmiller" +groupid: "Directory Administrators" + base: "o=Ace Industry, c=US" + filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" + base: "o=Ace Industry, c=US" + filter: "uid=hmiller" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +Authentication succeeded. + +Start of test: ./auth 3 "scarter" "Directory Administrators" + +uid: "scarter" +groupid: "Directory Administrators" + base: "o=Ace Industry, c=US" + filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" + base: "o=Ace Industry, c=US" + filter: "uid=scarter" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 4 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "Directory Administrators" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +groupid: "Directory Administrators" + base: "o=Ace Industry, c=US" + filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +Authentication succeeded. + +Start of test: ./auth 4 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "Directory Administrators" + +userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" +groupid: "Directory Administrators" + base: "o=Ace Industry, c=US" + filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" + base: "cn=Directory Administrators, o=Ace Industry, c=US" + filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + base: "o=Ace Industry, c=US" + filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 5 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "mail=hmiller@aceindustry.com" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +attrFilter: "mail=hmiller@aceindustry.com" + base: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + filter: "mail=hmiller@aceindustry.com" + scope: "LDAP_SCOPE_BASE" +Authentication succeeded. + +Start of test: ./auth 5 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "mail=scarter@aceindustry.com" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +attrFilter: "mail=scarter@aceindustry.com" + base: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + filter: "mail=scarter@aceindustry.com" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 5 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "mail=hmiller@aceindustry.com" + +userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" +attrFilter: "mail=hmiller@aceindustry.com" + base: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" + filter: "mail=hmiller@aceindustry.com" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 6 "hmiller" "mail=hmiller@aceindustry.com" + +uid: "hmiller" +attrFilter: "mail=hmiller@aceindustry.com" + base: "o=Ace Industry, c=US" + filter: "(& (uid=hmiller) (mail=hmiller@aceindustry.com))" + scope: "LDAP_SCOPE_SUBTREE" +Authentication succeeded. + +Start of test: ./auth 6 "hmiller" "mail=scarter@aceindustry.com" + +uid: "hmiller" +attrFilter: "mail=scarter@aceindustry.com" + base: "o=Ace Industry, c=US" + filter: "(& (uid=hmiller) (mail=scarter@aceindustry.com))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 6 "scarter" "mail=hmiller@aceindustry.com" + +uid: "scarter" +attrFilter: "mail=hmiller@aceindustry.com" + base: "o=Ace Industry, c=US" + filter: "(& (uid=scarter) (mail=hmiller@aceindustry.com))" + scope: "LDAP_SCOPE_SUBTREE" +ldap_search_s: Entry not found +Authentication failed. + +Start of test: ./auth 7 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "hillock" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +password: "hillock" + userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + password: "hillock" +Authentication succeeded. + +Start of test: ./auth 7 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "garbage" + +userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" +password: "garbage" + userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + password: "garbage" +ldap_simple_bind_s: Invalid credentials +Authentication failed. + +Start of test: ./auth 8 "hmiller" "hillock" + +uid: "hmiller" +password: "hillock" + base: "o=Ace Industry, c=US" + filter: "uid=hmiller" + scope: "LDAP_SCOPE_SUBTREE" + userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + password: "hillock" +Authentication succeeded. + +Start of test: ./auth 8 "hmiller" "garbage" + +uid: "hmiller" +password: "garbage" + base: "o=Ace Industry, c=US" + filter: "uid=hmiller" + scope: "LDAP_SCOPE_SUBTREE" + userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" + password: "garbage" +ldap_simple_bind_s: Invalid credentials +Authentication failed. + +Start of test: ./auth 9 "o=Ace Industry, c=US" "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" +Cert Map issuer DN: "o=Ace Industry, c=US" +Cert Map subject DN: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" +plugin_init_fn called. +plugin_init_fn called. +plugin_mapping_fn called. +Value of attrs from subject DN & issuer DN: + Cert: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" + subjdn: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" + cn: "Kirsten Vaughan" + ou: "Human Resources" + o: "Ace Industry" + c: "US" + issuerDN: "o=Ace Industry, c=US" + cn: *** Empty *** + ou: *** Empty *** + o: "Ace Industry" + c: "US" +plugin_search_fn called. + base: "ou=Human Resources, o=Ace Industry, c=US" + filter: "cn=Kirsten Vaughan" + scope: "LDAP_SCOPE_BASE" +ldap_search_s: Entry not found + base: "ou=Human Resources, o=Ace Industry, c=US" + filter: "cn=Kirsten Vaughan" + scope: "LDAP_SCOPE_SUBTREE" +plugin_verify_fn called. +Matched entry to cert: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US" +Authentication succeeded. + +Start of test: ./auth 10 "cn=Foo Bar, o=Netscape Communication, c=US" "foobar" +Config file written: config_out.conf +binddn from config file: "cn=Foo Bar, o=Netscape Communication, c=US" +bindpw from config file: "foobar" +Config file test succeeded +Authentication succeeded. + +Start of test: ./auth 11 "dblist.conf" "ignore" + dbname[0] = "default" + dbname[1] = "default1" + dbname[2] = "default2" + dbname[3] = "default3" + dbname[4] = "default4" + dbname[5] = "default5" + dbname[6] = "default6" +Authentication succeeded. + +Start of test: ./auth 12 "certmap.new" "certmap.conf" +plugin_init_fn called. +plugin_init_fn called. +Authentication succeeded. + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default +#default:DNComps o, ou ,c +#default:FilterComps cn + + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l +#default1:verifycert + +# Following line has trailing spaces +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l + +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + + +Start of test: ./auth 13 "certmap.new" "certmap.conf" +plugin_init_fn called. +plugin_init_fn called. +Authentication succeeded. + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default +#default:DNComps o, ou ,c +#default:FilterComps cn + + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l +#default1:verifycert + +# Following line has trailing spaces +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default + +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + + +Start of test: ./auth 14 "certmap.new" "certmap.conf" +plugin_init_fn called. +plugin_init_fn called. +Authentication succeeded. + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default +#default:DNComps o, ou ,c +#default:FilterComps cn + + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l +#default1:verifycert + +# Following line has trailing spaces +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + +# Comments before any certmap directive - line 1 +# Comments before any certmap directive - line 2 + +# Comments before any certmap directive - line 3 +# Comments before any certmap directive - line 4 +# Comments before any certmap directive - line 5 + + +# Comments before any certmap directive - line 6 + +certmap default default + +certmap default1 o=Netscape Communications, c=US +default1:library ./plugin.so +default1:InitFn plugin_init_fn +default1:DNComps ou o c +default1:FilterComps l + +certmap default2 o=Ace Industry, c=US +default2:InitFn plugin_init_fn +default2:DNComps cn o ou c +default2:FilterComps l +default2:verifycert on + +certmap newmap o=Mcom Communications, c=US +newmap:prop1 val1 +newmap:prop2 val2 +newmap:prop3 + diff --git a/lib/ldaputil/vtable.c b/lib/ldaputil/vtable.c new file mode 100644 index 00000000..b50678c4 --- /dev/null +++ b/lib/ldaputil/vtable.c @@ -0,0 +1,427 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include "ldaputili.h" +#include <ldap.h> +#ifdef USE_LDAP_SSL +#include <ldap_ssl.h> +#endif + +#if defined( _WINDOWS ) && ! defined( _WIN32 ) +/* On 16-bit WINDOWS platforms, it's erroneous to call LDAP API functions + * via a function pointer, since they are not declared LDAP_CALLBACK. + * So, we define the following functions, which are LDAP_CALLBACK, and + * simply delegate to their counterparts in the LDAP API. + */ + +#ifdef USE_LDAP_SSL +static LDAP_CALL LDAP_CALLBACK LDAP* +ldapuVd_ssl_init( const char *host, int port, int encrypted ) +{ + return ldapssl_init (host, port, encrypted); +} +#else +static LDAP_CALL LDAP_CALLBACK LDAP* +ldapuVd_init ( const char *host, int port ) +{ + return ldap_init (host, port); +} +#endif + +static LDAP_CALL LDAP_CALLBACK int +ldapuVd_set_option( LDAP *ld, int opt, void *val ) +{ + return ldap_set_option (ld, opt, val); +} + +static LDAP_CALL LDAP_CALLBACK int +ldapuVd_simple_bind_s( LDAP* ld, const char *username, const char *passwd ) +{ + return ldap_simple_bind_s (ld, username, passwd); +} + +static LDAP_CALL LDAP_CALLBACK int +ldapuVd_unbind( LDAP *ld ) +{ + return ldap_unbind (ld); +} + +static LDAP_CALL LDAP_CALLBACK int +ldapuVd_search_s( LDAP* ld, const char* baseDN, int scope, const char* filter, + char** attrs, int attrsonly, LDAPMessage** result ) +{ + return ldap_search_s (ld, baseDN, scope, filter, attrs, attrsonly, result); +} + +static LDAP_CALL LDAP_CALLBACK int +ldapuVd_count_entries( LDAP* ld, LDAPMessage* msg ) +{ + return ldap_count_entries (ld, msg); +} + +static LDAP_CALL LDAP_CALLBACK LDAPMessage* +ldapuVd_first_entry( LDAP* ld, LDAPMessage* msg ) +{ + return ldap_first_entry (ld, msg); +} + +static LDAP_CALL LDAP_CALLBACK LDAPMessage* +ldapuVd_next_entry( LDAP* ld, LDAPMessage* entry ) +{ + return ldap_next_entry(ld, entry); +} + +static LDAP_CALL LDAP_CALLBACK char* +ldapuVd_get_dn( LDAP* ld, LDAPMessage* entry ) +{ + return ldap_get_dn (ld, entry); +} + +static LDAP_CALL LDAP_CALLBACK char* +ldapuVd_first_attribute( LDAP* ld, LDAPMessage* entry, BerElement** iter ) +{ + return ldap_first_attribute (ld, entry, iter); +} + +static LDAP_CALL LDAP_CALLBACK char* +ldapuVd_next_attribute( LDAP* ld, LDAPMessage* entry, BerElement* iter) +{ + return ldap_next_attribute (ld, entry, iter); +} + +static LDAP_CALL LDAP_CALLBACK char** +ldapuVd_get_values( LDAP *ld, LDAPMessage *entry, const char *desc ) +{ + return ldap_get_values (ld, entry, desc); +} + +static LDAP_CALL LDAP_CALLBACK struct berval** +ldapuVd_get_values_len( LDAP *ld, LDAPMessage *entry, const char *desc ) +{ + return ldap_get_values_len (ld, entry, desc); +} + +#else +/* On other platforms, an LDAP API function can be called via a pointer. */ +#ifdef USE_LDAP_SSL +#define ldapuVd_ssl_init ldapssl_init +#else +#define ldapuVd_init ldap_init +#endif +#define ldapuVd_set_option ldap_set_option +#define ldapuVd_simple_bind_s ldap_simple_bind_s +#define ldapuVd_unbind ldap_unbind +#define ldapuVd_set_option ldap_set_option +#define ldapuVd_simple_bind_s ldap_simple_bind_s +#define ldapuVd_unbind ldap_unbind +#define ldapuVd_search_s ldap_search_s +#define ldapuVd_count_entries ldap_count_entries +#define ldapuVd_first_entry ldap_first_entry +#define ldapuVd_next_entry ldap_next_entry +#define ldapuVd_get_dn ldap_get_dn +#define ldapuVd_first_attribute ldap_first_attribute +#define ldapuVd_next_attribute ldap_next_attribute +#define ldapuVd_get_values ldap_get_values +#define ldapuVd_get_values_len ldap_get_values_len + +#endif + +/* Several functions in the standard LDAP API have no LDAP* parameter, + but all the VTable functions do. Here are some little functions that + make up the difference, by ignoring their LDAP* parameter: +*/ +static int LDAP_CALL LDAP_CALLBACK +ldapuVd_msgfree( LDAP *ld, LDAPMessage *chain ) +{ + return ldap_msgfree (chain); +} + +static void LDAP_CALL LDAP_CALLBACK +ldapuVd_memfree( LDAP *ld, void *dn ) +{ + ldap_memfree (dn); +} + +static void LDAP_CALL LDAP_CALLBACK +ldapuVd_ber_free( LDAP *ld, BerElement *ber, int freebuf ) +{ + ldap_ber_free (ber, freebuf); +} + +static void LDAP_CALL LDAP_CALLBACK +ldapuVd_value_free( LDAP *ld, char **vals ) +{ + ldap_value_free (vals); +} + +static void LDAP_CALL LDAP_CALLBACK +ldapuVd_value_free_len( LDAP *ld, struct berval **vals ) +{ + ldap_value_free_len (vals); +} + +static LDAPUVTable_t ldapu_VTable = { +/* By default, the VTable points to the standard LDAP API. */ +#ifdef USE_LDAP_SSL + ldapuVd_ssl_init, +#else + ldapuVd_init, +#endif + ldapuVd_set_option, + ldapuVd_simple_bind_s, + ldapuVd_unbind, + ldapuVd_search_s, + ldapuVd_count_entries, + ldapuVd_first_entry, + ldapuVd_next_entry, + ldapuVd_msgfree, + ldapuVd_get_dn, + ldapuVd_memfree, + ldapuVd_first_attribute, + ldapuVd_next_attribute, + ldapuVd_ber_free, + ldapuVd_get_values, + ldapuVd_value_free, + ldapuVd_get_values_len, + ldapuVd_value_free_len +}; + +/* Replace ldapu_VTable. Subsequently, ldaputil will call the + functions in 'from' (not the LDAP API) to access the directory. + */ +void +ldapu_VTable_set (LDAPUVTable_t* from) +{ + if (from) { + memcpy (&ldapu_VTable, from, sizeof(LDAPUVTable_t)); + } +} + +#ifdef USE_LDAP_SSL +LDAP* +ldapu_ssl_init( const char *defhost, int defport, int defsecure ) +{ + if (ldapu_VTable.ldapuV_ssl_init) { + return ldapu_VTable.ldapuV_ssl_init (defhost, defport, defsecure); + } + return NULL; +} +#else +LDAP* +ldapu_init( const char *defhost, int defport ) +{ + if (ldapu_VTable.ldapuV_init) { + return ldapu_VTable.ldapuV_init (defhost, defport); + } + return NULL; +} +#endif + +int +ldapu_set_option( LDAP *ld, int option, void *optdata ) +{ + if (ldapu_VTable.ldapuV_set_option) { + return ldapu_VTable.ldapuV_set_option (ld, option, optdata); + } + return LDAP_LOCAL_ERROR; +} + +int +ldapu_simple_bind_s( LDAP *ld, const char *who, const char *passwd ) +{ + if (ldapu_VTable.ldapuV_simple_bind_s) { + return ldapu_VTable.ldapuV_simple_bind_s (ld, who, passwd); + } + return LDAP_LOCAL_ERROR; +} + +int +ldapu_unbind( LDAP *ld ) +{ + if (ldapu_VTable.ldapuV_unbind) { + return ldapu_VTable.ldapuV_unbind (ld); + } + return LDAP_LOCAL_ERROR; +} + +int +ldapu_search_s( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, LDAPMessage **res ) +{ + if (ldapu_VTable.ldapuV_search_s) { + return ldapu_VTable.ldapuV_search_s (ld, base, scope, filter, attrs, attrsonly, res); + } + return LDAP_LOCAL_ERROR; +} + +int +ldapu_count_entries( LDAP *ld, LDAPMessage *chain ) +{ + if (ldapu_VTable.ldapuV_count_entries) { + return ldapu_VTable.ldapuV_count_entries (ld, chain); + } + return 0; +} + +LDAPMessage* +ldapu_first_entry( LDAP *ld, LDAPMessage *chain ) +{ + if (ldapu_VTable.ldapuV_first_entry) { + return ldapu_VTable.ldapuV_first_entry (ld, chain); + } + return NULL; +} + +LDAPMessage* +ldapu_next_entry( LDAP *ld, LDAPMessage *entry ) +{ + if (ldapu_VTable.ldapuV_next_entry) { + return ldapu_VTable.ldapuV_next_entry (ld, entry); + } + return NULL; +} + +int +ldapu_msgfree( LDAP* ld, LDAPMessage *chain ) +{ + if (ldapu_VTable.ldapuV_msgfree) { + return ldapu_VTable.ldapuV_msgfree (ld, chain); + } + return LDAP_SUCCESS; +} + +char* +ldapu_get_dn( LDAP *ld, LDAPMessage *entry ) +{ + if (ldapu_VTable.ldapuV_get_dn) { + return ldapu_VTable.ldapuV_get_dn (ld, entry); + } + return NULL; +} + +void +ldapu_memfree( LDAP* ld, void *p ) +{ + if (ldapu_VTable.ldapuV_memfree) { + ldapu_VTable.ldapuV_memfree (ld, p); + } +} + +char* +ldapu_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **ber ) +{ + if (ldapu_VTable.ldapuV_first_attribute) { + return ldapu_VTable.ldapuV_first_attribute (ld, entry, ber); + } + return NULL; +} + +char* +ldapu_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber ) +{ + if (ldapu_VTable.ldapuV_next_attribute) { + return ldapu_VTable.ldapuV_next_attribute (ld, entry, ber); + } + return NULL; +} + +void +ldapu_ber_free( LDAP* ld, BerElement *ber, int freebuf ) +{ + if (ldapu_VTable.ldapuV_ber_free) { + ldapu_VTable.ldapuV_ber_free (ld, ber, freebuf); + } +} + +char** +ldapu_get_values( LDAP *ld, LDAPMessage *entry, const char *desc ) +{ + if (ldapu_VTable.ldapuV_get_values) { + return ldapu_VTable.ldapuV_get_values (ld, entry, desc); + } else if (!ldapu_VTable.ldapuV_value_free + && ldapu_VTable.ldapuV_get_values_len) { + auto struct berval** bvals = + ldapu_VTable.ldapuV_get_values_len (ld, entry, desc); + if (bvals) { + auto char** vals = (char**) + ldapu_malloc ((ldap_count_values_len (bvals) + 1) + * sizeof(char*)); + if (vals) { + auto char** val; + auto struct berval** bval; + for (val = vals, bval = bvals; *bval; ++val, ++bval) { + auto const size_t len = (*bval)->bv_len; + *val = (char*) ldapu_malloc (len + 1); + memcpy (*val, (*bval)->bv_val, len); + (*val)[len] = '\0'; + } + *val = NULL; + ldapu_value_free_len(ld, bvals); + return vals; + } + } + ldapu_value_free_len(ld, bvals); + } + return NULL; +} + +void +ldapu_value_free( LDAP *ld, char **vals ) +{ + if (ldapu_VTable.ldapuV_value_free) { + ldapu_VTable.ldapuV_value_free (ld, vals); + } else if (!ldapu_VTable.ldapuV_get_values && vals) { + auto char** val; + for (val = vals; *val; ++val) { + free (*val); + } + free (vals); + } +} + +struct berval** +ldapu_get_values_len( LDAP *ld, LDAPMessage *entry, const char *desc ) +{ + if (ldapu_VTable.ldapuV_get_values_len) { + return ldapu_VTable.ldapuV_get_values_len (ld, entry, desc); + } else if (!ldapu_VTable.ldapuV_value_free_len + && ldapu_VTable.ldapuV_get_values) { + auto char** vals = + ldapu_VTable.ldapuV_get_values (ld, entry, desc); + if (vals) { + auto struct berval** bvals = (struct berval**) + ldapu_malloc ((ldap_count_values (vals) + 1) + * sizeof(struct berval*)); + if (bvals) { + auto char** val; + auto struct berval** bval; + for (val = vals, bval = bvals; *val; ++val, ++bval) { + auto const size_t len = strlen(*val); + *bval = (struct berval*) ldapu_malloc (sizeof(struct berval) + len); + (*bval)->bv_len = len; + (*bval)->bv_val = ((char*)(*bval)) + sizeof(struct berval); + memcpy ((*bval)->bv_val, *val, len); + } + *bval = NULL; + return bvals; + } + } + } + return NULL; +} + +void +ldapu_value_free_len( LDAP *ld, struct berval **vals ) +{ + if (ldapu_VTable.ldapuV_value_free_len) { + ldapu_VTable.ldapuV_value_free_len (ld, vals); + } else if (!ldapu_VTable.ldapuV_get_values_len && vals) { + auto struct berval** val; + for (val = vals; *val; ++val) { + free (*val); + } + free (vals); + } +} diff --git a/lib/libaccess/Makefile b/lib/libaccess/Makefile new file mode 100644 index 00000000..1ed3b58a --- /dev/null +++ b/lib/libaccess/Makefile @@ -0,0 +1,176 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Makefile for libaccess.a +# +MCOM_ROOT=../../.. +MODULE=LibAccess +include ../../nsdefs.mk + +OBJDEST=$(OBJDIR)/lib/libaccess +UTESTDEST=$(OBJDIR)/lib/libaccess/utest +LEX=flex + +include ../../nsconfig.mk + +MCC_INCLUDE += $(ADMINUTIL_INCLUDE) + +ifeq ($(ARCH), WINNT) +LIBS=$(OBJDIR)/lib/libaccess.lib +CC=cl -nologo -MT +VALUES=$(OBJDEST)/values.h +else +VALUES= +LIBS=$(OBJDIR)/lib/libaccess.a +endif + +all: $(OBJDEST) $(LIBS) + +$(OBJDEST): + mkdir -p $(OBJDEST) + +$(UTESTDEST): + mkdir -p $(UTESTDEST) + +OSOBJS = + +OBJS=$(addprefix $(OBJDEST)/, usi.o \ + attrec.o \ + nseframe.o \ + nsdb.o \ + nsdbmgmt.o \ + nsuser.o \ + nsumgmt.o \ + nsgroup.o \ + nsgmgmt.o \ + nsadb.o \ + nscert.o \ + nsamgmt.o \ + nsautherr.o \ + symbols.o \ + acltools.o \ + aclutil.o \ + aclcache.o \ + aclflush.o \ + authdb.o \ + method.o \ + ldapacl.o \ + register.o \ + lasdns.o \ + lasip.o \ + lastod.o \ + usrcache.o \ + lasgroup.o \ + lasuser.o \ + lasprogram.o \ + aclspace.o \ + acl.tab.o \ + acl.yy.o \ + acleval.o \ + oneeval.o \ + access_plhash.o \ + aclparse.o \ + aclbuild.o \ + aclerror.o \ + acladmin.o \ + aclcgi.o \ + $(OSOBJS) \ + ) + +# +# AVA Mapping files. Currently not compiled in (FORTEZZA for reference only). +# +AVAMAPFILES = lex.yy.o y.tab.o avapfile.o avadb.o + +MODULE_CFLAGS=-I$(NSROOT)/include -DACL_LIB_INTERNAL $(TESTFLAGS) + +ifeq ($(LDAP_NO_LIBLCACHE),1) +MODULE_CFLAGS+=-DNO_LIBLCACHE +endif + +LOCAL_DEPS = $(LDAPSDK_DEP) + +$(LIBS): $(LOCAL_DEPS) $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ + +include $(INCLUDE_DEPENDS) + +# +# acl.tab.c acl.tab.h and acl.yy.c should not be generated by the build, +# they are checked in and should be pulled from the tree by +# default. The following rules are provided in case the grammar or +# lexer needs changes. +# + +# +# Right now it's best to run yacc on a Solaris machine because the +# /usr/lib/yaccpar makes the NT compiler happier. It should work on +# other UNIX systems -- but that's what is checked in and tested. +# +yacc: + $(YACC) -d acltext.y + sed -f yy-sed y.tab.h > acl.tab.h + sed -f yy-sed y.tab.c > acl.tab.cpp + rm y.tab.h y.tab.c + +# +# Flex generates a case insenitive lexer. It also provides mechanisms +# that allow NSPR to replace it's standard IO routines. The standard UNIX +# lex wants to use some stupid library! One would think that lex could +# generate all the code it needs just like FLEX. +# +flex: + $(LEX) -i aclscan.l + sed -f yy-sed lex.yy.c > acl.yy.cpp + rm lex.yy.c + +# +# more AVA mapping stuff, needs to be made to work with the other lexx/yacc +# code added for 3.0 +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/y.tab.o: $(OBJDEST)/y.tab.c $(VALUES) $(OBJDEST)/y.tab.h +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I$(OBJDEST) -I. $< -Fo$*.o +#else +#$(OBJDEST)/y.tab.o: $(OBJDEST)/y.tab.c $(OBJDEST)/y.tab.h +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I. -o $*.o $< +#endif +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/y.tab.h: wintab.h +# cp wintab.h $(OBJDEST)/y.tab.h +# +#$(OBJDEST)/y.tab.c: winnt.y +# cp winnt.y $(OBJDEST)/y.tab.c +# +#$(OBJDEST)/values.h: winnt.v +# cp winnt.v $(OBJDEST)/values.h +#else +#$(OBJDEST)/y.tab.h $(OBJDEST)/y.tab.c: avaparse.y +# yacc -d avaparse.y +# mv y.tab.h $(OBJDEST)/ +# mv y.tab.c $(OBJDEST)/ +#endif +# +#$(OBJDEST)/lex.yy.o: $(OBJDEST)/lex.yy.c $(OBJDEST)/y.tab.h +#ifeq ($(ARCH), WINNT) +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I$(OBJDEST) -I. $< -Fo$*.o +#else +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I. -o $*.o $< +#endif +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/lex.yy.c: winnt.l +# cp winnt.l $(OBJDEST)/lex.yy.c +#else +#$(OBJDEST)/lex.yy.c: avascan.l +# lex avascan.l +# mv lex.yy.c $(OBJDEST)/ +#endif diff --git a/lib/libaccess/access_plhash.cpp b/lib/libaccess/access_plhash.cpp new file mode 100644 index 00000000..7666e95e --- /dev/null +++ b/lib/libaccess/access_plhash.cpp @@ -0,0 +1,65 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + This file contains a function which augments the standard nspr PL_HashTable + api. The problem is that the hash table lookup function in the standard NSPR + actually modifies the hash table being searched, which means that it cannot be + used with read locks in a multi threaded environment. This function is a + lookup function which is guaranteed not to modify the hash table passed in, + so that it can be used with read locks. +*/ + +#include "plhash.h" + +/* prototypes */ +NSPR_BEGIN_EXTERN_C +PR_IMPLEMENT(void *) +ACL_HashTableLookup_const(PLHashTable *ht, const void *key); +NSPR_END_EXTERN_C + +/* +** Multiplicative hash, from Knuth 6.4. +*/ +#define GOLDEN_RATIO 0x9E3779B9U + +PR_IMPLEMENT(PLHashEntry **) +ACL_HashTableRawLookup_const(PLHashTable *ht, PLHashNumber keyHash, const void *key) +{ + PLHashEntry *he, **hep; + PLHashNumber h; + +#ifdef HASHMETER + ht->nlookups++; +#endif + h = keyHash * GOLDEN_RATIO; + h >>= ht->shift; + hep = &ht->buckets[h]; + while ((he = *hep) != 0) { + if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { + return hep; + } + hep = &he->next; +#ifdef HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +PR_IMPLEMENT(void *) +ACL_HashTableLookup_const(PLHashTable *ht, const void *key) +{ + PLHashNumber keyHash; + PLHashEntry *he, **hep; + + keyHash = (*ht->keyHash)(key); + hep = ACL_HashTableRawLookup_const(ht, keyHash, key); + if ((he = *hep) != 0) { + return he->value; + } + return 0; +} diff --git a/lib/libaccess/access_plhash.h b/lib/libaccess/access_plhash.h new file mode 100644 index 00000000..fb1d86a1 --- /dev/null +++ b/lib/libaccess/access_plhash.h @@ -0,0 +1,17 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +NSPR_BEGIN_EXTERN_C +/* + * access_plhash.cpp - supplement to NSPR plhash + */ +extern void * +ACL_HashTableLookup_const( + void *ht, /* really a PLHashTable */ + const void *key); + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/acl.tab.cpp b/lib/libaccess/acl.tab.cpp new file mode 100644 index 00000000..bc17be3d --- /dev/null +++ b/lib/libaccess/acl.tab.cpp @@ -0,0 +1,1718 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +# line 8 "acltext.y" +#include <string.h> +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/nserror.h> +#include "parse.h" +#include "aclscan.h" + +#define MAX_LIST_SIZE 255 +static ACLListHandle_t *curr_acl_list; /* current acl list */ +static ACLHandle_t *curr_acl; /* current acl */ +static ACLExprHandle_t *curr_expr; /* current expression */ +static PFlags_t pflags; /* current authorization flags */ +static char *curr_args_list[MAX_LIST_SIZE]; /* current args */ +static char *curr_user_list[MAX_LIST_SIZE]; /* current users v2 */ +static char *curr_ip_dns_list[MAX_LIST_SIZE]; /* current ip/dns v2 */ +static PList_t curr_auth_info; /* current authorization method */ +static int use_generic_rights; /* use generic rights for conversion */ + +int acl_PushListHandle(ACLListHandle_t *handle) +{ + curr_acl_list = handle; + return(0); +} + +static void +acl_string_lower(char *s) +{ +int ii; +int len; + + len = strlen(s); + for (ii = 0; ii < len; ii++) + s[ii] = tolower(s[ii]); + + return; +} + +static void +acl_clear_args(char **args_list) +{ + args_list[0] = NULL; +} + +static void +acl_add_arg(char **args_list, char *arg) +{ + static int args_index; + + if ( args_list[0] == NULL ) { + args_index = 0; + } + args_list[args_index] = arg; + args_index++; + args_list[args_index] = NULL; +} + +static void +acl_free_args(char **args_list) +{ + int ii; + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) + PERM_FREE(args_list[ii]); + else + break; + } +} + +static int +acl_set_args(ACLExprHandle_t *expr, char **args_list) +{ + int ii; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) { + if ( ACL_ExprAddArg(NULL, expr, args_list[ii]) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + } else + break; + } + return(0); +} + +static int +acl_set_users(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_users_or_groups(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + if ( ACL_ExprTerm(NULL, expr, "group", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < (ii * 2) - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_ip_dns(ACLExprHandle_t *expr, char **ip_dns) +{ + int ii; + int jj; + int len; + char *attr; + char *val; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( ip_dns[ii] ) { + + attr = "ip"; + val = ip_dns[ii]; + len = strlen(val); + + for (jj = 0; jj < len; jj++) { + if ( strchr("0123456789.*", val[jj]) == 0 ) { + attr = "dns"; + break; + } + } + + if ( ACL_ExprTerm(NULL, expr, attr, CMP_OP_EQ, + val) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(ip_dns); + return(-1); + } + + } else + break; + } + + acl_free_args(ip_dns); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + + return(0); +} + + + +# line 223 "acltext.y" +typedef union +#ifdef __cplusplus + ACLSTYPE +#endif + { + char *string; + int ival; +} ACLSTYPE; +# define ACL_ABSOLUTE_TOK 257 +# define ACL_ACL_TOK 258 +# define ACL_ALLOW_TOK 259 +# define ACL_ALWAYS_TOK 260 +# define ACL_AND_TOK 261 +# define ACL_AT_TOK 262 +# define ACL_AUTHENTICATE_TOK 263 +# define ACL_CONTENT_TOK 264 +# define ACL_DEFAULT_TOK 265 +# define ACL_DENY_TOK 266 +# define ACL_GROUP_TOK 267 +# define ACL_IN_TOK 268 +# define ACL_INHERIT_TOK 269 +# define ACL_NOT_TOK 270 +# define ACL_NULL_TOK 271 +# define ACL_OR_TOK 272 +# define ACL_QSTRING_TOK 273 +# define ACL_READ_TOK 274 +# define ACL_TERMINAL_TOK 275 +# define ACL_VARIABLE_TOK 276 +# define ACL_VERSION_TOK 277 +# define ACL_WRITE_TOK 278 +# define ACL_WITH_TOK 279 +# define ACL_EQ_TOK 280 +# define ACL_GE_TOK 281 +# define ACL_GT_TOK 282 +# define ACL_LE_TOK 283 +# define ACL_LT_TOK 284 +# define ACL_NE_TOK 285 + +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#else +#include <netsite.h> +#include <memory.h> +#endif + + +#ifdef __cplusplus + +#ifndef aclerror + void aclerror(const char *); +#endif + +#ifndef acllex +#ifdef __EXTERN_C__ + extern "C" { int acllex(void); } +#else + int acllex(void); +#endif +#endif + int acl_Parse(void); + +#endif +#define aclclearin aclchar = -1 +#define aclerrok aclerrflag = 0 +extern int aclchar; +extern int aclerrflag; +ACLSTYPE acllval; +ACLSTYPE aclval; +typedef int acltabelem; +#ifndef ACLMAXDEPTH +#define ACLMAXDEPTH 150 +#endif +#if ACLMAXDEPTH > 0 +int acl_acls[ACLMAXDEPTH], *acls = acl_acls; +ACLSTYPE acl_aclv[ACLMAXDEPTH], *aclv = acl_aclv; +#else /* user does initial allocation */ +int *acls; +ACLSTYPE *aclv; +#endif +static int aclmaxdepth = ACLMAXDEPTH; +# define ACLERRCODE 256 + +# line 952 "acltext.y" + +acltabelem aclexca[] ={ +-1, 1, + 0, -1, + -2, 0, + }; +# define ACLNPROD 120 +# define ACLLAST 251 +acltabelem aclact[]={ + + 176, 177, 178, 180, 179, 181, 156, 109, 69, 53, + 160, 116, 76, 6, 185, 169, 118, 186, 170, 117, + 150, 78, 85, 149, 77, 18, 144, 29, 17, 86, + 28, 11, 3, 126, 10, 136, 140, 82, 89, 104, + 87, 101, 7, 129, 127, 171, 133, 79, 72, 40, + 132, 38, 102, 55, 108, 37, 172, 105, 39, 60, + 60, 107, 128, 63, 59, 45, 61, 61, 93, 23, + 46, 6, 131, 130, 158, 142, 137, 157, 125, 134, + 154, 147, 56, 122, 112, 30, 75, 94, 81, 111, + 139, 138, 88, 73, 165, 164, 155, 57, 50, 49, + 48, 27, 14, 41, 65, 58, 145, 97, 153, 146, + 98, 152, 120, 25, 184, 151, 119, 24, 99, 64, + 13, 32, 15, 21, 5, 175, 159, 106, 103, 8, + 100, 124, 84, 83, 66, 54, 52, 143, 80, 51, + 67, 90, 36, 35, 26, 34, 33, 22, 31, 20, + 135, 113, 62, 74, 96, 47, 92, 71, 44, 68, + 43, 70, 42, 95, 16, 91, 9, 4, 19, 12, + 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 115, 114, 121, 123, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 95, 141, 0, + 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 163, 0, 0, 0, 166, + 167, 168, 0, 0, 0, 0, 174, 0, 173, 0, + 161, 0, 0, 0, 118, 78, 162, 117, 77, 182, + 183 }; +acltabelem aclpact[]={ + + -245,-10000000,-10000000, -234, -187,-10000000, -242,-10000000,-10000000, 80, +-10000000,-10000000, 43, -248, -189, 76, 69,-10000000,-10000000,-10000000, + -189,-10000000, 42, -246, -38, -248,-10000000, -208,-10000000,-10000000, + -195,-10000000,-10000000, -208, 41, 40, 39,-10000000,-10000000, -270, + -213, -43, 38,-10000000,-10000000, -199, -200,-10000000,-10000000,-10000000, +-10000000, 79,-10000000,-10000000,-10000000, -271,-10000000, -195,-10000000, -220, +-10000000,-10000000, -28, -221, -239,-10000000, -235, -238,-10000000,-10000000, +-10000000, -28,-10000000,-10000000, -194,-10000000, -252,-10000000,-10000000,-10000000, + 66,-10000000,-10000000,-10000000, 78, -223, -218, -203,-10000000, -273, + -238,-10000000, -39, -29, 75, 68, -39, -40, -239, -243, +-10000000, -231, -202,-10000000, -232, -184,-10000000, -185, -214, -227, +-10000000,-10000000, -241,-10000000,-10000000,-10000000, -257, -240,-10000000,-10000000, + -252,-10000000, -250,-10000000, 65,-10000000,-10000000,-10000000,-10000000,-10000000, +-10000000,-10000000,-10000000,-10000000, -44, -241, -253, 74, 67, 64, +-10000000,-10000000, -45, 37, -274, -30, -243,-10000000,-10000000, 36, + 35,-10000000, -257, -257,-10000000, -250, -258,-10000000, -216,-10000000, + -30, -30, -280,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000, +-10000000, -30, -30, 73,-10000000, -259,-10000000,-10000000,-10000000,-10000000, +-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000 }; +acltabelem aclpgo[]={ + + 0, 171, 170, 169, 168, 167, 124, 166, 122, 103, + 164, 162, 160, 158, 105, 157, 93, 156, 89, 154, + 153, 151, 86, 87, 91, 90, 76, 79, 150, 149, + 123, 147, 121, 146, 145, 143, 142, 141, 92, 140, + 139, 138, 75, 88, 137, 136, 104, 135, 134, 133, + 132, 131, 77, 130, 128, 127, 78, 74, 126, 125 }; +acltabelem aclr1[]={ + + 0, 1, 1, 3, 1, 2, 5, 5, 6, 7, + 7, 8, 8, 10, 10, 9, 9, 11, 11, 15, + 13, 13, 14, 14, 17, 12, 19, 12, 16, 16, + 20, 20, 23, 23, 22, 22, 21, 21, 21, 24, + 24, 25, 26, 26, 26, 26, 18, 28, 28, 27, + 27, 4, 29, 29, 30, 30, 31, 31, 32, 32, + 33, 33, 33, 37, 36, 39, 36, 38, 40, 34, + 41, 41, 43, 42, 42, 44, 44, 45, 35, 47, + 35, 48, 46, 49, 50, 50, 50, 50, 50, 50, + 50, 55, 55, 55, 55, 53, 53, 53, 53, 54, + 54, 54, 54, 51, 51, 56, 52, 52, 52, 57, + 57, 57, 58, 58, 59, 59, 59, 59, 59, 59 }; +acltabelem aclr2[]={ + + 0, 0, 2, 1, 10, 2, 2, 4, 17, 3, + 3, 2, 6, 3, 3, 4, 6, 2, 2, 1, + 8, 6, 3, 3, 1, 10, 1, 10, 7, 3, + 2, 6, 2, 6, 3, 3, 2, 2, 6, 3, + 3, 5, 2, 2, 6, 6, 7, 7, 7, 2, + 4, 2, 2, 4, 6, 4, 5, 5, 2, 4, + 4, 4, 4, 1, 10, 1, 8, 7, 1, 17, + 2, 6, 3, 4, 6, 7, 7, 1, 6, 1, + 6, 1, 5, 10, 0, 3, 5, 3, 5, 3, + 5, 3, 3, 5, 5, 3, 3, 5, 5, 3, + 3, 5, 5, 2, 6, 3, 2, 7, 7, 2, + 6, 5, 7, 7, 2, 2, 2, 2, 2, 2 }; +acltabelem aclchk[]={ + +-10000000, -1, -2, 277, -5, -6, 258, 276, -6, -7, + 276, 273, -3, 40, 59, -8, -10, 276, 273, -4, + -29, -30, -31, 258, 41, 44, -30, 59, 276, 273, + 123, -8, -32, -33, -34, -35, -36, 263, 259, 266, + 257, -9, -11, -12, -13, 260, 265, -32, 59, 59, + 59, -40, -45, 279, -47, 266, 125, 59, -14, 263, + 259, 266, -14, 263, 40, -46, -48, -39, -46, 279, + -9, -15, 268, -16, -20, -22, 40, 276, 273, 268, + -41, -43, 276, -49, -50, 257, 264, 275, -38, 276, + -37, -16, -17, 262, -23, -22, -19, 41, 44, 40, + -53, 264, 275, -54, 257, 275, -55, 264, 257, 280, + -38, -18, 123, -21, -24, -25, 40, 276, 273, 41, + 44, -18, 123, -43, -51, -56, 276, 275, 264, 275, + 257, 257, 264, 273, -27, -28, 276, -26, -24, -25, + 276, -23, -42, -44, 276, 41, 44, 125, -27, 276, + 273, 41, 44, 44, 125, 59, 280, -52, -57, -58, + 40, 270, 276, -56, 59, 59, -26, -26, -42, 273, + 276, 261, 272, -52, -57, -59, 280, 281, 282, 284, + 283, 285, -52, -52, 41, 273, 276 }; +acltabelem acldef[]={ + + 1, -2, 2, 0, 5, 6, 0, 3, 7, 0, + 9, 10, 0, 0, 0, 0, 11, 13, 14, 4, + 51, 52, 0, 0, 0, 0, 53, 55, 56, 57, + 0, 12, 54, 58, 0, 0, 0, 68, 77, 79, + 0, 0, 0, 17, 18, 0, 0, 59, 60, 61, + 62, 0, 81, 65, 81, 0, 8, 15, 19, 0, + 22, 23, 0, 0, 0, 78, 84, 0, 80, 63, + 16, 0, 24, 21, 29, 30, 0, 34, 35, 26, + 0, 70, 72, 82, 0, 85, 87, 89, 66, 0, + 0, 20, 0, 0, 0, 32, 0, 0, 0, 0, + 86, 95, 96, 88, 99, 100, 90, 91, 92, 0, + 64, 25, 0, 28, 36, 37, 0, 39, 40, 31, + 0, 27, 0, 71, 0, 103, 105, 97, 98, 101, + 102, 93, 94, 67, 0, 49, 0, 0, 42, 43, + 41, 33, 0, 0, 0, 0, 0, 46, 50, 0, + 0, 38, 0, 0, 69, 73, 0, 83, 106, 109, + 0, 0, 0, 104, 47, 48, 44, 45, 74, 75, + 76, 0, 0, 0, 111, 0, 114, 115, 116, 117, + 118, 119, 107, 108, 110, 112, 113 }; +typedef struct +#ifdef __cplusplus + acltoktype +#endif +{ char *t_name; int t_val; } acltoktype; +#ifndef ACLDEBUG +# define ACLDEBUG 0 /* don't allow debugging */ +#endif + +#if ACLDEBUG + +acltoktype acltoks[] = +{ + "ACL_ABSOLUTE_TOK", 257, + "ACL_ACL_TOK", 258, + "ACL_ALLOW_TOK", 259, + "ACL_ALWAYS_TOK", 260, + "ACL_AND_TOK", 261, + "ACL_AT_TOK", 262, + "ACL_AUTHENTICATE_TOK", 263, + "ACL_CONTENT_TOK", 264, + "ACL_DEFAULT_TOK", 265, + "ACL_DENY_TOK", 266, + "ACL_GROUP_TOK", 267, + "ACL_IN_TOK", 268, + "ACL_INHERIT_TOK", 269, + "ACL_NOT_TOK", 270, + "ACL_NULL_TOK", 271, + "ACL_OR_TOK", 272, + "ACL_QSTRING_TOK", 273, + "ACL_READ_TOK", 274, + "ACL_TERMINAL_TOK", 275, + "ACL_VARIABLE_TOK", 276, + "ACL_VERSION_TOK", 277, + "ACL_WRITE_TOK", 278, + "ACL_WITH_TOK", 279, + "ACL_EQ_TOK", 280, + "ACL_GE_TOK", 281, + "ACL_GT_TOK", 282, + "ACL_LE_TOK", 283, + "ACL_LT_TOK", 284, + "ACL_NE_TOK", 285, + "-unknown-", -1 /* ends search */ +}; + +char * aclreds[] = +{ + "-no such reduction-", + "start : /* empty */", + "start : start_acl_v2", + "start : ACL_VERSION_TOK ACL_VARIABLE_TOK", + "start : ACL_VERSION_TOK ACL_VARIABLE_TOK ';' start_acl_v3", + "start_acl_v2 : acl_list_v2", + "acl_list_v2 : acl_v2", + "acl_list_v2 : acl_list_v2 acl_v2", + "acl_v2 : ACL_ACL_TOK acl_name_v2 '(' arg_list_v2 ')' '{' directive_list_v2 '}'", + "acl_name_v2 : ACL_VARIABLE_TOK", + "acl_name_v2 : ACL_QSTRING_TOK", + "arg_list_v2 : arg_v2", + "arg_list_v2 : arg_v2 ',' arg_list_v2", + "arg_v2 : ACL_VARIABLE_TOK", + "arg_v2 : ACL_QSTRING_TOK", + "directive_list_v2 : directive_v2 ';'", + "directive_list_v2 : directive_v2 ';' directive_list_v2", + "directive_v2 : auth_method_v2", + "directive_v2 : auth_statement_v2", + "auth_statement_v2 : ACL_ALWAYS_TOK auth_type_v2", + "auth_statement_v2 : ACL_ALWAYS_TOK auth_type_v2 host_spec_list_action_v2", + "auth_statement_v2 : ACL_DEFAULT_TOK auth_type_v2 host_spec_list_action_v2", + "auth_type_v2 : ACL_ALLOW_TOK", + "auth_type_v2 : ACL_DENY_TOK", + "auth_method_v2 : ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK", + "auth_method_v2 : ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK realm_definition_v2", + "auth_method_v2 : ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK", + "auth_method_v2 : ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK realm_definition_v2", + "host_spec_list_action_v2 : user_expr_v2 ACL_AT_TOK host_spec_list_v2", + "host_spec_list_action_v2 : user_expr_v2", + "user_expr_v2 : user_v2", + "user_expr_v2 : '(' user_list_v2 ')'", + "user_list_v2 : user_v2", + "user_list_v2 : user_v2 ',' user_list_v2", + "user_v2 : ACL_VARIABLE_TOK", + "user_v2 : ACL_QSTRING_TOK", + "host_spec_list_v2 : dns_spec_v2", + "host_spec_list_v2 : ip_spec_v2", + "host_spec_list_v2 : '(' dns_ip_spec_list_v2 ')'", + "dns_spec_v2 : ACL_VARIABLE_TOK", + "dns_spec_v2 : ACL_QSTRING_TOK", + "ip_spec_v2 : ACL_VARIABLE_TOK ACL_VARIABLE_TOK", + "dns_ip_spec_list_v2 : dns_spec_v2", + "dns_ip_spec_list_v2 : ip_spec_v2", + "dns_ip_spec_list_v2 : dns_spec_v2 ',' dns_ip_spec_list_v2", + "dns_ip_spec_list_v2 : ip_spec_v2 ',' dns_ip_spec_list_v2", + "realm_definition_v2 : '{' methods_list_v2 '}'", + "method_v2 : ACL_VARIABLE_TOK ACL_VARIABLE_TOK ';'", + "method_v2 : ACL_VARIABLE_TOK ACL_QSTRING_TOK ';'", + "methods_list_v2 : method_v2", + "methods_list_v2 : method_v2 methods_list_v2", + "start_acl_v3 : acl_list", + "acl_list : acl", + "acl_list : acl_list acl", + "acl : named_acl ';' body_list", + "acl : named_acl ';'", + "named_acl : ACL_ACL_TOK ACL_VARIABLE_TOK", + "named_acl : ACL_ACL_TOK ACL_QSTRING_TOK", + "body_list : body", + "body_list : body body_list", + "body : authenticate_statement ';'", + "body : authorization_statement ';'", + "body : deny_statement ';'", + "deny_statement : ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK", + "deny_statement : ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK deny_common", + "deny_statement : ACL_DENY_TOK ACL_WITH_TOK", + "deny_statement : ACL_DENY_TOK ACL_WITH_TOK deny_common", + "deny_common : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK", + "authenticate_statement : ACL_AUTHENTICATE_TOK", + "authenticate_statement : ACL_AUTHENTICATE_TOK '(' attribute_list ')' '{' parameter_list '}'", + "attribute_list : attribute", + "attribute_list : attribute_list ',' attribute", + "attribute : ACL_VARIABLE_TOK", + "parameter_list : parameter ';'", + "parameter_list : parameter ';' parameter_list", + "parameter : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK", + "parameter : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK", + "authorization_statement : ACL_ALLOW_TOK", + "authorization_statement : ACL_ALLOW_TOK auth_common_action", + "authorization_statement : ACL_DENY_TOK", + "authorization_statement : ACL_DENY_TOK auth_common_action", + "auth_common_action : /* empty */", + "auth_common_action : auth_common", + "auth_common : flag_list '(' args_list ')' expression", + "flag_list : /* empty */", + "flag_list : ACL_ABSOLUTE_TOK", + "flag_list : ACL_ABSOLUTE_TOK content_static", + "flag_list : ACL_CONTENT_TOK", + "flag_list : ACL_CONTENT_TOK absolute_static", + "flag_list : ACL_TERMINAL_TOK", + "flag_list : ACL_TERMINAL_TOK content_absolute", + "content_absolute : ACL_CONTENT_TOK", + "content_absolute : ACL_ABSOLUTE_TOK", + "content_absolute : ACL_CONTENT_TOK ACL_ABSOLUTE_TOK", + "content_absolute : ACL_ABSOLUTE_TOK ACL_CONTENT_TOK", + "content_static : ACL_CONTENT_TOK", + "content_static : ACL_TERMINAL_TOK", + "content_static : ACL_CONTENT_TOK ACL_TERMINAL_TOK", + "content_static : ACL_TERMINAL_TOK ACL_CONTENT_TOK", + "absolute_static : ACL_ABSOLUTE_TOK", + "absolute_static : ACL_TERMINAL_TOK", + "absolute_static : ACL_ABSOLUTE_TOK ACL_TERMINAL_TOK", + "absolute_static : ACL_TERMINAL_TOK ACL_ABSOLUTE_TOK", + "args_list : arg", + "args_list : args_list ',' arg", + "arg : ACL_VARIABLE_TOK", + "expression : factor", + "expression : factor ACL_AND_TOK expression", + "expression : factor ACL_OR_TOK expression", + "factor : base_expr", + "factor : '(' expression ')'", + "factor : ACL_NOT_TOK factor", + "base_expr : ACL_VARIABLE_TOK relop ACL_QSTRING_TOK", + "base_expr : ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK", + "relop : ACL_EQ_TOK", + "relop : ACL_GE_TOK", + "relop : ACL_GT_TOK", + "relop : ACL_LT_TOK", + "relop : ACL_LE_TOK", + "relop : ACL_NE_TOK", +}; +#endif /* ACLDEBUG */ + + +/* +** Skeleton parser driver for yacc output +*/ + +/* +** yacc user known macros and defines +*/ +#define ACLERROR goto aclerrlab +#define ACLACCEPT return(0) +#define ACLABORT return(1) +#define ACLBACKUP( newtoken, newvalue )\ +{\ + if ( aclchar >= 0 || ( aclr2[ acltmp ] >> 1 ) != 1 )\ + {\ + aclerror( "syntax error - cannot backup" );\ + goto aclerrlab;\ + }\ + aclchar = newtoken;\ + aclstate = *aclps;\ + acllval = newvalue;\ + goto aclnewstate;\ +} +#define ACLRECOVERING() (!!aclerrflag) +#define ACLNEW(type) PERM_MALLOC(sizeof(type) * aclnewmax) +#define ACLCOPY(to, from, type) \ + (type *) memcpy(to, (char *) from, aclnewmax * sizeof(type)) +#define ACLENLARGE( from, type) \ + (type *) PERM_REALLOC((char *) from, aclnewmax * sizeof(type)) +#ifndef ACLDEBUG +# define ACLDEBUG 1 /* make debugging available */ +#endif + +/* +** user known globals +*/ +int acldebug; /* set to 1 to get debugging */ + +/* +** driver internal defines +*/ +#define ACLFLAG (-10000000) + +/* +** global variables used by the parser +*/ +ACLSTYPE *aclpv; /* top of value stack */ +int *aclps; /* top of state stack */ + +int aclstate; /* current state */ +int acltmp; /* extra var (lasts between blocks) */ + +int aclnerrs; /* number of errors */ +int aclerrflag; /* error recovery flag */ +int aclchar; /* current input token number */ + + + +#ifdef ACLNMBCHARS +#define ACLLEX() aclcvtok(acllex()) +/* +** aclcvtok - return a token if i is a wchar_t value that exceeds 255. +** If i<255, i itself is the token. If i>255 but the neither +** of the 30th or 31st bit is on, i is already a token. +*/ +#if defined(__STDC__) || defined(__cplusplus) +int aclcvtok(int i) +#else +int aclcvtok(i) int i; +#endif +{ + int first = 0; + int last = ACLNMBCHARS - 1; + int mid; + wchar_t j; + + if(i&0x60000000){/*Must convert to a token. */ + if( aclmbchars[last].character < i ){ + return i;/*Giving up*/ + } + while ((last>=first)&&(first>=0)) {/*Binary search loop*/ + mid = (first+last)/2; + j = aclmbchars[mid].character; + if( j==i ){/*Found*/ + return aclmbchars[mid].tvalue; + }else if( j<i ){ + first = mid + 1; + }else{ + last = mid -1; + } + } + /*No entry in the table.*/ + return i;/* Giving up.*/ + }else{/* i is already a token. */ + return i; + } +} +#else/*!ACLNMBCHARS*/ +#define ACLLEX() acllex() +#endif/*!ACLNMBCHARS*/ + +/* +** acl_Parse - return 0 if worked, 1 if syntax error not recovered from +*/ +#if defined(__STDC__) || defined(__cplusplus) +int acl_Parse(void) +#else +int acl_Parse() +#endif +{ + register ACLSTYPE *aclpvt; /* top of value stack for $vars */ + +#if defined(__cplusplus) || defined(lint) +/* + hacks to please C++ and lint - goto's inside switch should never be + executed; aclpvt is set to 0 to avoid "used before set" warning. +*/ + static int __yaccpar_lint_hack__ = 0; + switch (__yaccpar_lint_hack__) + { + case 1: goto aclerrlab; + case 2: goto aclnewstate; + } + aclpvt = 0; +#endif + + /* + ** Initialize externals - acl_Parse may be called more than once + */ + aclpv = &aclv[-1]; + aclps = &acls[-1]; + aclstate = 0; + acltmp = 0; + aclnerrs = 0; + aclerrflag = 0; + aclchar = -1; + +#if ACLMAXDEPTH <= 0 + if (aclmaxdepth <= 0) + { + if ((aclmaxdepth = ACLEXPAND(0)) <= 0) + { + aclerror("yacc initialization error"); + ACLABORT; + } + } +#endif + + { + register ACLSTYPE *acl_pv; /* top of value stack */ + register int *acl_ps; /* top of state stack */ + register int acl_state; /* current state */ + register int acl_n; /* internal state number info */ + goto aclstack; /* moved from 6 lines above to here to please C++ */ + + /* + ** get globals into registers. + ** branch to here only if ACLBACKUP was called. + */ + aclnewstate: + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + goto acl_newstate; + + /* + ** get globals into registers. + ** either we just started, or we just finished a reduction + */ + aclstack: + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + + /* + ** top of for (;;) loop while no reductions done + */ + acl_stack: + /* + ** put a state and value onto the stacks + */ +#if ACLDEBUG + /* + ** if debugging, look up token value in list of value vs. + ** name pairs. 0 and negative (-1) are special values. + ** Note: linear search is used since time is not a real + ** consideration while debugging. + */ + if ( acldebug ) + { + register int acl_i; + + printf( "State %d, token ", acl_state ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val == aclchar ) + break; + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( ++acl_ps >= &acls[ aclmaxdepth ] ) /* room on stack? */ + { + /* + ** reallocate and recover. Note that pointers + ** have to be reset, or bad things will happen + */ + int aclps_index = (acl_ps - acls); + int aclpv_index = (acl_pv - aclv); + int aclpvt_index = (aclpvt - aclv); + int aclnewmax; +#ifdef ACLEXPAND + aclnewmax = ACLEXPAND(aclmaxdepth); +#else + aclnewmax = 2 * aclmaxdepth; /* double table size */ + if (aclmaxdepth == ACLMAXDEPTH) /* first time growth */ + { + char *newacls = (char *)ACLNEW(int); + char *newaclv = (char *)ACLNEW(ACLSTYPE); + if (newacls != 0 && newaclv != 0) + { + acls = ACLCOPY(newacls, acls, int); + aclv = ACLCOPY(newaclv, aclv, ACLSTYPE); + } + else + aclnewmax = 0; /* failed */ + } + else /* not first time */ + { + acls = ACLENLARGE(acls, int); + aclv = ACLENLARGE(aclv, ACLSTYPE); + if (acls == 0 || aclv == 0) + aclnewmax = 0; /* failed */ + } +#endif + if (aclnewmax <= aclmaxdepth) /* tables not expanded */ + { + aclerror( "yacc stack overflow" ); + ACLABORT; + } + aclmaxdepth = aclnewmax; + + acl_ps = acls + aclps_index; + acl_pv = aclv + aclpv_index; + aclpvt = aclv + aclpvt_index; + } + *acl_ps = acl_state; + *++acl_pv = aclval; + + /* + ** we have a new state - find out what to do + */ + acl_newstate: + if ( ( acl_n = aclpact[ acl_state ] ) <= ACLFLAG ) + goto acldefault; /* simple state */ +#if ACLDEBUG + /* + ** if debugging, need to mark whether new token grabbed + */ + acltmp = aclchar < 0; +#endif + if ( ( aclchar < 0 ) && ( ( aclchar = ACLLEX() ) < 0 ) ) + aclchar = 0; /* reached EOF */ +#if ACLDEBUG + if ( acldebug && acltmp ) + { + register int acl_i; + + printf( "Received token " ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val == aclchar ) + break; + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( ( ( acl_n += aclchar ) < 0 ) || ( acl_n >= ACLLAST ) ) + goto acldefault; + if ( aclchk[ acl_n = aclact[ acl_n ] ] == aclchar ) /*valid shift*/ + { + aclchar = -1; + aclval = acllval; + acl_state = acl_n; + if ( aclerrflag > 0 ) + aclerrflag--; + goto acl_stack; + } + + acldefault: + if ( ( acl_n = acldef[ acl_state ] ) == -2 ) + { +#if ACLDEBUG + acltmp = aclchar < 0; +#endif + if ( ( aclchar < 0 ) && ( ( aclchar = ACLLEX() ) < 0 ) ) + aclchar = 0; /* reached EOF */ +#if ACLDEBUG + if ( acldebug && acltmp ) + { + register int acl_i; + + printf( "Received token " ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; + acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val + == aclchar ) + { + break; + } + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + /* + ** look through exception table + */ + { + register int *aclxi = aclexca; + + while ( ( *aclxi != -1 ) || + ( aclxi[1] != acl_state ) ) + { + aclxi += 2; + } + while ( ( *(aclxi += 2) >= 0 ) && + ( *aclxi != aclchar ) ) + ; + if ( ( acl_n = aclxi[1] ) < 0 ) + ACLACCEPT; + } + } + + /* + ** check for syntax error + */ + if ( acl_n == 0 ) /* have an error */ + { + /* no worry about speed here! */ + switch ( aclerrflag ) + { + case 0: /* new error */ + aclerror( "syntax error" ); + goto skip_init; + aclerrlab: + /* + ** get globals into registers. + ** we have a user generated syntax type error + */ + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + skip_init: + aclnerrs++; + /* FALLTHRU */ + case 1: + case 2: /* incompletely recovered error */ + /* try again... */ + aclerrflag = 3; + /* + ** find state where "error" is a legal + ** shift action + */ + while ( acl_ps >= acls ) + { + acl_n = aclpact[ *acl_ps ] + ACLERRCODE; + if ( acl_n >= 0 && acl_n < ACLLAST && + aclchk[aclact[acl_n]] == ACLERRCODE) { + /* + ** simulate shift of "error" + */ + acl_state = aclact[ acl_n ]; + goto acl_stack; + } + /* + ** current state has no shift on + ** "error", pop stack + */ +#if ACLDEBUG +# define _POP_ "Error recovery pops state %d, uncovers state %d\n" + if ( acldebug ) + printf( _POP_, *acl_ps, + acl_ps[-1] ); +# undef _POP_ +#endif + acl_ps--; + acl_pv--; + } + /* + ** there is no state on stack with "error" as + ** a valid shift. give up. + */ + ACLABORT; + case 3: /* no shift yet; eat a token */ +#if ACLDEBUG + /* + ** if debugging, look up token in list of + ** pairs. 0 and negative shouldn't occur, + ** but since timing doesn't matter when + ** debugging, it doesn't hurt to leave the + ** tests here. + */ + if ( acldebug ) + { + register int acl_i; + + printf( "Error recovery discards " ); + if ( aclchar == 0 ) + printf( "token end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "token -none-\n" ); + else + { + for ( acl_i = 0; + acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val + == aclchar ) + { + break; + } + } + printf( "token %s\n", + acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( aclchar == 0 ) /* reached EOF. quit */ + ACLABORT; + aclchar = -1; + goto acl_newstate; + } + }/* end if ( acl_n == 0 ) */ + /* + ** reduction by production acl_n + ** put stack tops, etc. so things right after switch + */ +#if ACLDEBUG + /* + ** if debugging, print the string that is the user's + ** specification of the reduction which is just about + ** to be done. + */ + if ( acldebug ) + printf( "Reduce by (%d) \"%s\"\n", + acl_n, aclreds[ acl_n ] ); +#endif + acltmp = acl_n; /* value to switch over */ + aclpvt = acl_pv; /* $vars top of value stack */ + /* + ** Look in goto table for next state + ** Sorry about using acl_state here as temporary + ** register variable, but why not, if it works... + ** If aclr2[ acl_n ] doesn't have the low order bit + ** set, then there is no action to be done for + ** this reduction. So, no saving & unsaving of + ** registers done. The only difference between the + ** code just after the if and the body of the if is + ** the goto acl_stack in the body. This way the test + ** can be made before the choice of what to do is needed. + */ + { + /* length of production doubled with extra bit */ + register int acl_len = aclr2[ acl_n ]; + + if ( !( acl_len & 01 ) ) + { + acl_len >>= 1; + aclval = ( acl_pv -= acl_len )[1]; /* $$ = $1 */ + acl_state = aclpgo[ acl_n = aclr1[ acl_n ] ] + + *( acl_ps -= acl_len ) + 1; + if ( acl_state >= ACLLAST || + aclchk[ acl_state = + aclact[ acl_state ] ] != -acl_n ) + { + acl_state = aclact[ aclpgo[ acl_n ] ]; + } + goto acl_stack; + } + acl_len >>= 1; + aclval = ( acl_pv -= acl_len )[1]; /* $$ = $1 */ + acl_state = aclpgo[ acl_n = aclr1[ acl_n ] ] + + *( acl_ps -= acl_len ) + 1; + if ( acl_state >= ACLLAST || + aclchk[ acl_state = aclact[ acl_state ] ] != -acl_n ) + { + acl_state = aclact[ aclpgo[ acl_n ] ]; + } + } + /* save until reenter driver code */ + aclstate = acl_state; + aclps = acl_ps; + aclpv = acl_pv; + } + /* + ** code supplied by user is placed in this switch + */ + switch( acltmp ) + { + +case 3: +# line 266 "acltext.y" +{ + PERM_FREE(aclpvt[-0].string); + } break; +case 8: +# line 286 "acltext.y" +{ + acl_free_args(curr_args_list); + } break; +case 9: +# line 292 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } break; +case 10: +# line 314 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } break; +case 13: +# line 342 "acltext.y" +{ + char acl_tmp_arg[255]; + char *acl_new_arg; + + if (!use_generic_rights) { + acl_string_lower(aclpvt[-0].string); + strcpy(acl_tmp_arg, "http_"); + strcat(acl_tmp_arg, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + acl_new_arg = PERM_STRDUP(acl_tmp_arg); + acl_add_arg(curr_args_list, acl_new_arg); + } else { + PERM_FREE(aclpvt[-0].string); + } + } break; +case 14: +# line 358 "acltext.y" +{ + if (!use_generic_rights) { + acl_add_arg(curr_args_list, aclpvt[-0].string); + } else { + PERM_FREE(aclpvt[-0].string); + } + } break; +case 19: +# line 376 "acltext.y" +{ + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } + } break; +case 22: +# line 388 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } break; +case 23: +# line 398 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } break; +case 24: +# line 411 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(auth) failed"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 26: +# line 430 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(auth) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 28: +# line 446 "acltext.y" +{ + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + aclerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_ip_dns(curr_expr, curr_ip_dns_list) < 0 ) { + aclerror("acl_set_ip_dns() failed"); + return(-1); + } + + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprAnd() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + aclerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 29: +# line 473 "acltext.y" +{ + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + aclerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + aclerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 34: +# line 500 "acltext.y" +{ + acl_add_arg(curr_user_list, aclpvt[-0].string); + } break; +case 35: +# line 504 "acltext.y" +{ + acl_add_arg(curr_user_list, aclpvt[-0].string); + } break; +case 39: +# line 516 "acltext.y" +{ + acl_add_arg(curr_ip_dns_list, aclpvt[-0].string); + } break; +case 40: +# line 520 "acltext.y" +{ + acl_add_arg(curr_ip_dns_list, aclpvt[-0].string); + } break; +case 41: +# line 526 "acltext.y" +{ + char tmp_str[255]; + + util_sprintf(tmp_str, "%s+%s", aclpvt[-1].string, aclpvt[-0].string); + PERM_FREE(aclpvt[-1].string); + PERM_FREE(aclpvt[-0].string); + acl_add_arg(curr_ip_dns_list, PERM_STRDUP(tmp_str)); + } break; +case 46: +# line 543 "acltext.y" +{ + if ( ACL_ExprAddArg(NULL, curr_expr, "user") < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAddArg(NULL, curr_expr, "group") < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 47: +# line 562 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if (strcmp(aclpvt[-2].string, "database") == 0) { + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-1].string); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-1].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } + } break; +case 48: +# line 575 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if (strcmp(aclpvt[-2].string, "database") == 0) { + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-1].string); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-1].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } + } break; +case 56: +# line 611 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + } break; +case 57: +# line 620 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + } break; +case 63: +# line 641 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set deny processing flags"); + return(-1); + } + } break; +case 65: +# line 659 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 67: +# line 674 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprSetDenyWith(NULL, curr_expr, + aclpvt[-2].string, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprSetDenyWith() failed"); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; +case 68: +# line 687 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 69: +# line 701 "acltext.y" +{ + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 72: +# line 713 "acltext.y" +{ + acl_string_lower(aclpvt[-0].string); + if ( ACL_ExprAddArg(NULL, curr_expr, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + PERM_FREE(aclpvt[-0].string); + } break; +case 75: +# line 728 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-0].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } break; +case 76: +# line 736 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-0].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } break; +case 77: +# line 746 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + } break; +case 79: +# line 756 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + } break; +case 81: +# line 768 "acltext.y" +{ + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 82: +# line 775 "acltext.y" +{ + if ( ACL_ExprSetPFlags (NULL, curr_expr, pflags) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } +#ifdef DEBUG + if ( ACL_ExprDisplay(curr_expr) < 0 ) { + aclerror("ACL_ExprDisplay() failed"); + return(-1); + } + printf("Parsed authorization.\n"); +#endif + } break; +case 85: +# line 795 "acltext.y" +{ + pflags = ACL_PFLAG_ABSOLUTE; + } break; +case 86: +# line 799 "acltext.y" +{ + pflags = ACL_PFLAG_ABSOLUTE; + } break; +case 87: +# line 803 "acltext.y" +{ + pflags = ACL_PFLAG_CONTENT; + } break; +case 88: +# line 807 "acltext.y" +{ + pflags = ACL_PFLAG_CONTENT; + } break; +case 89: +# line 811 "acltext.y" +{ + pflags = ACL_PFLAG_TERMINAL; + } break; +case 90: +# line 815 "acltext.y" +{ + pflags = ACL_PFLAG_TERMINAL; + } break; +case 91: +# line 821 "acltext.y" +{ + pflags |= ACL_PFLAG_CONTENT; + } break; +case 92: +# line 825 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE; + } break; +case 93: +# line 829 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } break; +case 94: +# line 833 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } break; +case 95: +# line 839 "acltext.y" +{ + pflags |= ACL_PFLAG_CONTENT; + } break; +case 96: +# line 843 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL; + } break; +case 97: +# line 847 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } break; +case 98: +# line 851 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } break; +case 99: +# line 857 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE; + } break; +case 100: +# line 861 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL; + } break; +case 101: +# line 865 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } break; +case 102: +# line 869 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } break; +case 105: +# line 879 "acltext.y" +{ + acl_string_lower(aclpvt[-0].string); + if ( ACL_ExprAddArg(NULL, curr_expr, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + PERM_FREE( aclpvt[-0].string ); + } break; +case 107: +# line 891 "acltext.y" +{ + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprAnd() failed"); + return(-1); + } + } break; +case 108: +# line 898 "acltext.y" +{ + if ( ACL_ExprOr(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } break; +case 111: +# line 909 "acltext.y" +{ + if ( ACL_ExprNot(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprNot() failed"); + return(-1); + } + } break; +case 112: +# line 918 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprTerm(NULL, curr_expr, + aclpvt[-2].string, (CmpOp_t) aclpvt[-1].ival, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; +case 113: +# line 931 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprTerm(NULL, curr_expr, + aclpvt[-2].string, (CmpOp_t) aclpvt[-1].ival, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; + } + goto aclstack; /* reset registers in driver code */ +} + diff --git a/lib/libaccess/acl.tab.h b/lib/libaccess/acl.tab.h new file mode 100644 index 00000000..359f290a --- /dev/null +++ b/lib/libaccess/acl.tab.h @@ -0,0 +1,44 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef union +#ifdef __cplusplus + ACLSTYPE +#endif + { + char *string; + int ival; +} ACLSTYPE; +extern ACLSTYPE acllval; +# define ACL_ABSOLUTE_TOK 257 +# define ACL_ACL_TOK 258 +# define ACL_ALLOW_TOK 259 +# define ACL_ALWAYS_TOK 260 +# define ACL_AND_TOK 261 +# define ACL_AT_TOK 262 +# define ACL_AUTHENTICATE_TOK 263 +# define ACL_CONTENT_TOK 264 +# define ACL_DEFAULT_TOK 265 +# define ACL_DENY_TOK 266 +# define ACL_GROUP_TOK 267 +# define ACL_IN_TOK 268 +# define ACL_INHERIT_TOK 269 +# define ACL_NOT_TOK 270 +# define ACL_NULL_TOK 271 +# define ACL_OR_TOK 272 +# define ACL_QSTRING_TOK 273 +# define ACL_READ_TOK 274 +# define ACL_TERMINAL_TOK 275 +# define ACL_VARIABLE_TOK 276 +# define ACL_VERSION_TOK 277 +# define ACL_WRITE_TOK 278 +# define ACL_WITH_TOK 279 +# define ACL_EQ_TOK 280 +# define ACL_GE_TOK 281 +# define ACL_GT_TOK 282 +# define ACL_LE_TOK 283 +# define ACL_LT_TOK 284 +# define ACL_NE_TOK 285 diff --git a/lib/libaccess/acl.yy.cpp b/lib/libaccess/acl.yy.cpp new file mode 100644 index 00000000..90821330 --- /dev/null +++ b/lib/libaccess/acl.yy.cpp @@ -0,0 +1,1995 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define ACL_FLEX_MAJOR_VERSION 2 +#define ACL_FLEX_MINOR_VERSION 5 + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> + +/* Use prototypes in function declarations. */ +#define ACL_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define ACL_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define ACL_USE_PROTOS +#define ACL_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define ACL_USE_CONST +#define ACL_USE_PROTOS +#endif + +#ifdef ACL_USE_CONST +#define aclconst const +#else +#define aclconst +#endif + + +#ifdef ACL_USE_PROTOS +#define ACL_PROTO(proto) proto +#else +#define ACL_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define ACL_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define ACL_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN acl_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The ACLSTATE alias is for lex + * compatibility. + */ +#define ACL_START ((acl_start - 1) / 2) +#define ACLSTATE ACL_START + +/* Action number for EOF rule of a given start state. */ +#define ACL_STATE_EOF(state) (ACL_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define ACL_NEW_FILE aclrestart( aclin ) + +#define ACL_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define ACL_BUF_SIZE 16384 + +typedef struct acl_buffer_state *ACL_BUFFER_STATE; + +extern int aclleng; +extern FILE *aclin, *aclout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * aclless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the aclless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define aclless(n) \ + do \ + { \ + /* Undo effects of setting up acltext. */ \ + *acl_cp = acl_hold_char; \ + ACL_RESTORE_ACL_MORE_OFFSET \ + acl_c_buf_p = acl_cp = acl_bp + n - ACL_MORE_ADJ; \ + ACL_DO_BEFORE_ACTION; /* set up acltext again */ \ + } \ + while ( 0 ) + +#define unput(c) aclunput( c, acltext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int acl_size_t; + + +struct acl_buffer_state + { + FILE *acl_input_file; + + char *acl_ch_buf; /* input buffer */ + char *acl_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + acl_size_t acl_buf_size; + + /* Number of characters read into acl_ch_buf, not including EOB + * characters. + */ + int acl_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can PERM_REALLOC() it to grow it, and should PERM_FREE() it to + * delete it. + */ + int acl_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int acl_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int acl_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int acl_fill_buffer; + + int acl_buffer_status; +#define ACL_BUFFER_NEW 0 +#define ACL_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as ACL_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via aclrestart()), so that the user can continue scanning by + * just pointing aclin at a new input file. + */ +#define ACL_BUFFER_EOF_PENDING 2 + }; + +static ACL_BUFFER_STATE acl_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define ACL_CURRENT_BUFFER acl_current_buffer + + +/* acl_hold_char holds the character lost when acltext is formed. */ +static char acl_hold_char; + +static int acl_n_chars; /* number of characters read into acl_ch_buf */ + + +int aclleng; + +/* Points to current character in buffer. */ +static char *acl_c_buf_p = (char *) 0; +static int acl_init = 1; /* whether we need to initialize */ +static int acl_start = 0; /* start state number */ + +/* Flag which is used to allow aclwrap()'s to do buffer switches + * instead of setting up a fresh aclin. A bit of a hack ... + */ +static int acl_did_buffer_switch_on_eof; + +void aclrestart ACL_PROTO(( FILE *input_file )); + +void acl_switch_to_buffer ACL_PROTO(( ACL_BUFFER_STATE new_buffer )); +void acl_load_buffer_state ACL_PROTO(( void )); +ACL_BUFFER_STATE acl_create_buffer ACL_PROTO(( FILE *file, int size )); +void acl_delete_buffer ACL_PROTO(( ACL_BUFFER_STATE b )); +void acl_init_buffer ACL_PROTO(( ACL_BUFFER_STATE b, FILE *file )); +void acl_flush_buffer ACL_PROTO(( ACL_BUFFER_STATE b )); +#define ACL_FLUSH_BUFFER acl_flush_buffer( acl_current_buffer ) + +ACL_BUFFER_STATE acl_scan_buffer ACL_PROTO(( char *base, acl_size_t size )); +ACL_BUFFER_STATE acl_scan_string ACL_PROTO(( aclconst char *str )); +ACL_BUFFER_STATE acl_scan_bytes ACL_PROTO(( aclconst char *bytes, int len )); + +static void *ACL_FLEX_ALLOC ACL_PROTO(( acl_size_t )); +static void *ACL_FLEX_REALLOC ACL_PROTO(( void *, acl_size_t )); +static void ACL_FLEX_FREE ACL_PROTO(( void * )); + +#define acl_new_buffer acl_create_buffer + +#define acl_set_interactive(is_interactive) \ + { \ + if ( ! acl_current_buffer ) \ + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); \ + acl_current_buffer->acl_is_interactive = is_interactive; \ + } + +#define acl_set_bol(at_bol) \ + { \ + if ( ! acl_current_buffer ) \ + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); \ + acl_current_buffer->acl_at_bol = at_bol; \ + } + +#define ACL_AT_BOL() (acl_current_buffer->acl_at_bol) + +typedef unsigned char ACL_CHAR; +FILE *aclin = (FILE *) 0, *aclout = (FILE *) 0; +typedef int acl_state_type; +extern char *acltext; +#define acltext_ptr acltext + +static acl_state_type acl_get_previous_state ACL_PROTO(( void )); +static acl_state_type acl_try_NUL_trans ACL_PROTO(( acl_state_type current_state )); +static int acl_get_next_buffer ACL_PROTO(( void )); +static void acl_fatal_error ACL_PROTO(( aclconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up acltext. + */ +#define ACL_DO_BEFORE_ACTION \ + acltext_ptr = acl_bp; \ + aclleng = (int) (acl_cp - acl_bp); \ + acl_hold_char = *acl_cp; \ + *acl_cp = '\0'; \ + acl_c_buf_p = acl_cp; + +#define ACL_NUM_RULES 30 +#define ACL_END_OF_BUFFER 31 +static aclconst short int acl_accept[104] = + { 0, + 0, 0, 31, 30, 2, 1, 30, 30, 3, 28, + 29, 25, 22, 24, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 2, 1, 27, 0, 4, 3, 29, + 26, 23, 29, 29, 29, 29, 9, 29, 29, 29, + 14, 29, 21, 29, 29, 29, 29, 6, 29, 29, + 20, 29, 29, 29, 29, 29, 19, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 13, 29, 29, 29, + 18, 29, 7, 29, 29, 29, 29, 29, 29, 29, + 29, 8, 29, 29, 29, 29, 29, 29, 29, 29, + 11, 12, 15, 29, 17, 5, 29, 16, 29, 29, + + 29, 10, 0 + } ; + +static aclconst int acl_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 1, 1, 7, + 7, 8, 1, 7, 8, 8, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 1, 7, 9, + 10, 11, 1, 1, 12, 13, 14, 15, 16, 17, + 8, 18, 19, 8, 8, 20, 21, 22, 23, 8, + 8, 24, 25, 26, 27, 28, 29, 8, 30, 8, + 1, 1, 1, 1, 8, 1, 12, 13, 14, 15, + + 16, 17, 8, 18, 19, 8, 8, 20, 21, 22, + 23, 8, 8, 24, 25, 26, 27, 28, 29, 8, + 30, 8, 7, 1, 7, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static aclconst int acl_meta[31] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + } ; + +static aclconst short int acl_base[108] = + { 0, + 0, 0, 118, 119, 115, 0, 106, 28, 0, 119, + 0, 105, 119, 104, 21, 90, 96, 89, 87, 85, + 92, 91, 87, 103, 0, 119, 33, 119, 0, 0, + 119, 119, 79, 83, 17, 87, 0, 75, 78, 22, + 81, 72, 0, 73, 72, 69, 71, 0, 70, 80, + 0, 73, 64, 77, 58, 71, 0, 65, 60, 66, + 63, 53, 51, 64, 63, 51, 0, 53, 57, 56, + 0, 47, 0, 48, 50, 49, 50, 50, 46, 44, + 40, 0, 39, 38, 37, 36, 49, 38, 43, 39, + 0, 0, 0, 36, 0, 0, 36, 0, 33, 16, + + 24, 0, 119, 48, 51, 54, 29 + } ; + +static aclconst short int acl_def[108] = + { 0, + 103, 1, 103, 103, 103, 104, 103, 105, 106, 103, + 107, 103, 103, 103, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 103, 104, 103, 105, 103, 106, 107, + 103, 103, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + + 107, 107, 0, 103, 103, 103, 103 + } ; + +static aclconst short int acl_nxt[150] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 11, 16, 17, 11, 11, 11, 18, 11, + 11, 19, 20, 11, 11, 21, 11, 22, 23, 11, + 28, 30, 28, 33, 34, 28, 49, 28, 54, 102, + 35, 101, 36, 55, 100, 50, 37, 38, 25, 99, + 25, 27, 27, 27, 29, 98, 29, 97, 96, 95, + 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, + 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, + 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, + 64, 63, 62, 61, 60, 59, 58, 57, 56, 53, + + 52, 51, 48, 47, 24, 46, 45, 44, 43, 42, + 41, 40, 39, 32, 31, 26, 24, 103, 3, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103 + } ; + +static aclconst short int acl_chk[150] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 8, 107, 8, 15, 15, 27, 35, 27, 40, 101, + 15, 100, 15, 40, 99, 35, 15, 15, 104, 97, + 104, 105, 105, 105, 106, 94, 106, 90, 89, 88, + 87, 86, 85, 84, 83, 81, 80, 79, 78, 77, + 76, 75, 74, 72, 70, 69, 68, 66, 65, 64, + 63, 62, 61, 60, 59, 58, 56, 55, 54, 53, + 52, 50, 49, 47, 46, 45, 44, 42, 41, 39, + + 38, 36, 34, 33, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 14, 12, 7, 5, 3, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103 + } ; + +static acl_state_type acl_last_accepting_state; +static char *acl_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define aclmore() aclmore_used_but_not_detected +#define ACL_MORE_ADJ 0 +#define ACL_RESTORE_ACL_MORE_OFFSET +char *acltext; +#line 1 "aclscan.l" +#define INITIAL 0 +/* + * Lexical analyzer for ACL + */ +#line 6 "aclscan.l" +#include "acl.tab.h" /* token codes */ +#include <base/file.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "aclpriv.h" +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <libaccess/aclerror.h> +#ifdef XP_WIN32 +#include <io.h> +#endif + +#include "parse.h" +#include "aclscan.h" + +static int acl_scanner_input(char *buffer, int max_size); + +#define ACL_NEVER_INTERACTIVE 1 +#undef ACL_INPUT +#define ACL_INPUT(buf, result, max) (result = acl_scanner_input(buf, max)) + +static int acl_lineno; +static int acl_tokenpos; +static char acl_filename[500]; +static NSErr_t *acl_errp; +static int acl_use_buffer; +static char *acl_buffer; +static int acl_buffer_length; +static int acl_buffer_offset; +static char *last_string; +static SYS_FILE acl_prfd; + + +#line 466 "acl.yy.cpp" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef ACL_SKIP_ACLWRAP +#ifdef __cplusplus +extern "C" int aclwrap ACL_PROTO(( void )); +#else +extern int aclwrap ACL_PROTO(( void )); +#endif +#endif + +#ifndef ACL_NO_UNPUT +static void aclunput ACL_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef acltext_ptr +static void acl_flex_strncpy ACL_PROTO(( char *, aclconst char *, int )); +#endif + +#ifdef ACL_NEED_STRLEN +static int acl_flex_strlen ACL_PROTO(( aclconst char * )); +#endif + +#ifndef ACL_NO_INPUT +#ifdef __cplusplus +static int aclinput ACL_PROTO(( void )); +#else +static int input ACL_PROTO(( void )); +#endif +#endif + +#if ACL_STACK_USED +static int acl_start_stack_ptr = 0; +static int acl_start_stack_depth = 0; +static int *acl_start_stack = 0; +#ifndef ACL_NO_PUSH_STATE +static void acl_push_state ACL_PROTO(( int new_state )); +#endif +#ifndef ACL_NO_POP_STATE +static void acl_pop_state ACL_PROTO(( void )); +#endif +#ifndef ACL_NO_TOP_STATE +static int acl_top_state ACL_PROTO(( void )); +#endif + +#else +#define ACL_NO_PUSH_STATE 1 +#define ACL_NO_POP_STATE 1 +#define ACL_NO_TOP_STATE 1 +#endif + +#ifdef ACL_MALLOC_DECL +ACL_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef ACL_READ_BUF_SIZE +#define ACL_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( acltext, aclleng, 1, aclout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or ACL_NULL, + * is returned in "result". + */ +#ifndef ACL_INPUT +#define ACL_INPUT(buf,result,max_size) \ + if ( acl_current_buffer->acl_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( aclin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( aclin ) ) \ + ACL_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, aclin )) == 0) \ + && ferror( aclin ) ) \ + ACL_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "aclterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef aclterminate +#define aclterminate() return ACL_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef ACL_START_STACK_INCR +#define ACL_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef ACL_FATAL_ERROR +#define ACL_FATAL_ERROR(msg) acl_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef ACL_DECL +#define ACL_DECL int acllex ACL_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after acltext and aclleng + * have been set up. + */ +#ifndef ACL_USER_ACTION +#define ACL_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef ACL_BREAK +#define ACL_BREAK break; +#endif + +#define ACL_RULE_SETUP \ + ACL_USER_ACTION + +ACL_DECL + { + register acl_state_type acl_current_state; + register char *acl_cp, *acl_bp; + register int acl_act; + +#line 47 "aclscan.l" + + +#line 620 "acl.yy.cpp" + + if ( acl_init ) + { + acl_init = 0; + +#ifdef ACL_USER_INIT + ACL_USER_INIT; +#endif + + if ( ! acl_start ) + acl_start = 1; /* first start state */ + + if ( ! aclin ) + aclin = stdin; + + if ( ! aclout ) + aclout = stdout; + + if ( ! acl_current_buffer ) + acl_current_buffer = + acl_create_buffer( aclin, ACL_BUF_SIZE ); + + acl_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + acl_cp = acl_c_buf_p; + + /* Support of acltext. */ + *acl_cp = acl_hold_char; + + /* acl_bp points to the position in acl_ch_buf of the start of + * the current run. + */ + acl_bp = acl_cp; + + acl_current_state = acl_start; +acl_match: + do + { + register ACL_CHAR acl_c = acl_ec[ACL_SC_TO_UI(*acl_cp)]; + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + ++acl_cp; + } + while ( acl_base[acl_current_state] != 119 ); + +acl_find_action: + acl_act = acl_accept[acl_current_state]; + if ( acl_act == 0 ) + { /* have to back up */ + acl_cp = acl_last_accepting_cpos; + acl_current_state = acl_last_accepting_state; + acl_act = acl_accept[acl_current_state]; + } + + ACL_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( acl_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of ACL_DO_BEFORE_ACTION */ + *acl_cp = acl_hold_char; + acl_cp = acl_last_accepting_cpos; + acl_current_state = acl_last_accepting_state; + goto acl_find_action; + +case 1: +ACL_RULE_SETUP +#line 49 "aclscan.l" +{ + acl_lineno++; + acl_tokenpos = 0; + aclless(1); + } + ACL_BREAK +case 2: +ACL_RULE_SETUP +#line 55 "aclscan.l" +; + ACL_BREAK +case 3: +ACL_RULE_SETUP +#line 57 "aclscan.l" +; + ACL_BREAK +case ACL_STATE_EOF(INITIAL): +#line 59 "aclscan.l" +{ + acllval.string = NULL; + last_string = acllval.string; + return(0); + } + ACL_BREAK +case 4: +ACL_RULE_SETUP +#line 65 "aclscan.l" +{ + acllval.string = PERM_STRDUP( acltext+1 ); + last_string = acllval.string; + if ( acllval.string[aclleng-2] != '"' ) + fprintf(stderr, "Unterminated string\n") ; + else + acllval.string[aclleng-2] = '\0'; + acl_tokenpos += aclleng; + return ACL_QSTRING_TOK; + } + ACL_BREAK +case 5: +ACL_RULE_SETUP +#line 77 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ABSOLUTE_TOK; + } + ACL_BREAK +case 6: +ACL_RULE_SETUP +#line 83 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ACL_TOK; + } + ACL_BREAK +case 7: +ACL_RULE_SETUP +#line 89 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ALLOW_TOK; + } + ACL_BREAK +case 8: +ACL_RULE_SETUP +#line 95 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ALWAYS_TOK; + } + ACL_BREAK +case 9: +ACL_RULE_SETUP +#line 101 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_AT_TOK; + } + ACL_BREAK +case 10: +ACL_RULE_SETUP +#line 107 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_AUTHENTICATE_TOK; + } + ACL_BREAK +case 11: +ACL_RULE_SETUP +#line 113 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_CONTENT_TOK; + } + ACL_BREAK +case 12: +ACL_RULE_SETUP +#line 119 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_DEFAULT_TOK; + } + ACL_BREAK +case 13: +ACL_RULE_SETUP +#line 125 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_DENY_TOK; + } + ACL_BREAK +case 14: +ACL_RULE_SETUP +#line 131 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_IN_TOK; + } + ACL_BREAK +case 15: +ACL_RULE_SETUP +#line 137 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_INHERIT_TOK; + } + ACL_BREAK +case 16: +ACL_RULE_SETUP +#line 143 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_TERMINAL_TOK; + } + ACL_BREAK +case 17: +ACL_RULE_SETUP +#line 149 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_VERSION_TOK; + } + ACL_BREAK +case 18: +ACL_RULE_SETUP +#line 155 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_WITH_TOK; + } + ACL_BREAK +case 19: +ACL_RULE_SETUP +#line 161 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_NOT_TOK; + } + ACL_BREAK +case 20: +ACL_RULE_SETUP +#line 167 "aclscan.l" +{ + last_string = NULL; + acllval.ival = ACL_EXPR_OP_AND; + acl_tokenpos += aclleng; + return ACL_AND_TOK; + } + ACL_BREAK +case 21: +ACL_RULE_SETUP +#line 174 "aclscan.l" +{ + last_string = NULL; + acllval.ival = ACL_EXPR_OP_OR; + acl_tokenpos += aclleng; + return ACL_OR_TOK; + } + ACL_BREAK +case 22: +ACL_RULE_SETUP +#line 181 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_EQ; + acl_tokenpos += aclleng; + return ACL_EQ_TOK; + } + ACL_BREAK +case 23: +ACL_RULE_SETUP +#line 188 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_GE; + acl_tokenpos += aclleng; + return ACL_GE_TOK; + } + ACL_BREAK +case 24: +ACL_RULE_SETUP +#line 195 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_GT; + acl_tokenpos += aclleng; + return ACL_GT_TOK; + } + ACL_BREAK +case 25: +ACL_RULE_SETUP +#line 202 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_LT; + acl_tokenpos += aclleng; + return ACL_LT_TOK; + } + ACL_BREAK +case 26: +ACL_RULE_SETUP +#line 209 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_LE; + acl_tokenpos += aclleng; + return ACL_LE_TOK; + } + ACL_BREAK +case 27: +ACL_RULE_SETUP +#line 216 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_NE; + acl_tokenpos += aclleng; + return ACL_NE_TOK; + } + ACL_BREAK +case 28: +ACL_RULE_SETUP +#line 223 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return acltext[0]; + } + ACL_BREAK +case 29: +ACL_RULE_SETUP +#line 229 "aclscan.l" +{ + acl_tokenpos += aclleng; + acllval.string = PERM_STRDUP( acltext ); + last_string = acllval.string; + return ACL_VARIABLE_TOK; + } + ACL_BREAK +case 30: +ACL_RULE_SETUP +#line 235 "aclscan.l" +ECHO; + ACL_BREAK +#line 983 "acl.yy.cpp" + + case ACL_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int acl_amount_of_matched_text = (int) (acl_cp - acltext_ptr) - 1; + + /* Undo the effects of ACL_DO_BEFORE_ACTION. */ + *acl_cp = acl_hold_char; + ACL_RESTORE_ACL_MORE_OFFSET + + if ( acl_current_buffer->acl_buffer_status == ACL_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed aclin at a new source and called + * acllex(). If so, then we have to assure + * consistency between acl_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + acl_n_chars = acl_current_buffer->acl_n_chars; + acl_current_buffer->acl_input_file = aclin; + acl_current_buffer->acl_buffer_status = ACL_BUFFER_NORMAL; + } + + /* Note that here we test for acl_c_buf_p "<=" to the position + * of the first EOB in the buffer, since acl_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( acl_c_buf_p <= &acl_current_buffer->acl_ch_buf[acl_n_chars] ) + { /* This was really a NUL. */ + acl_state_type acl_next_state; + + acl_c_buf_p = acltext_ptr + acl_amount_of_matched_text; + + acl_current_state = acl_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * acl_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + acl_next_state = acl_try_NUL_trans( acl_current_state ); + + acl_bp = acltext_ptr + ACL_MORE_ADJ; + + if ( acl_next_state ) + { + /* Consume the NUL. */ + acl_cp = ++acl_c_buf_p; + acl_current_state = acl_next_state; + goto acl_match; + } + + else + { + acl_cp = acl_c_buf_p; + goto acl_find_action; + } + } + + else switch ( acl_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + acl_did_buffer_switch_on_eof = 0; + + if ( aclwrap() ) + { + /* Note: because we've taken care in + * acl_get_next_buffer() to have set up + * acltext, we can now set up + * acl_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * ACL_NULL, it'll still work - another + * ACL_NULL will get returned. + */ + acl_c_buf_p = acltext_ptr + ACL_MORE_ADJ; + + acl_act = ACL_STATE_EOF(ACL_START); + goto do_action; + } + + else + { + if ( ! acl_did_buffer_switch_on_eof ) + ACL_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + acl_c_buf_p = + acltext_ptr + acl_amount_of_matched_text; + + acl_current_state = acl_get_previous_state(); + + acl_cp = acl_c_buf_p; + acl_bp = acltext_ptr + ACL_MORE_ADJ; + goto acl_match; + + case EOB_ACT_LAST_MATCH: + acl_c_buf_p = + &acl_current_buffer->acl_ch_buf[acl_n_chars]; + + acl_current_state = acl_get_previous_state(); + + acl_cp = acl_c_buf_p; + acl_bp = acltext_ptr + ACL_MORE_ADJ; + goto acl_find_action; + } + break; + } + + default: + ACL_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of acllex */ + + +/* acl_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int acl_get_next_buffer() + { + register char *dest = acl_current_buffer->acl_ch_buf; + register char *source = acltext_ptr; + register int number_to_move, i; + int ret_val; + + if ( acl_c_buf_p > &acl_current_buffer->acl_ch_buf[acl_n_chars + 1] ) + ACL_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( acl_current_buffer->acl_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( acl_c_buf_p - acltext_ptr - ACL_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (acl_c_buf_p - acltext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( acl_current_buffer->acl_buffer_status == ACL_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + acl_n_chars = 0; + + else + { + int num_to_read = + acl_current_buffer->acl_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef ACL_USES_REJECT + ACL_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + ACL_BUFFER_STATE b = acl_current_buffer; + + int acl_c_buf_p_offset = + (int) (acl_c_buf_p - b->acl_ch_buf); + + if ( b->acl_is_our_buffer ) + { + int new_size = b->acl_buf_size * 2; + + if ( new_size <= 0 ) + b->acl_buf_size += b->acl_buf_size / 8; + else + b->acl_buf_size *= 2; + + b->acl_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + ACL_FLEX_REALLOC( (void *) b->acl_ch_buf, + b->acl_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->acl_ch_buf = 0; + + if ( ! b->acl_ch_buf ) + ACL_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + acl_c_buf_p = &b->acl_ch_buf[acl_c_buf_p_offset]; + + num_to_read = acl_current_buffer->acl_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > ACL_READ_BUF_SIZE ) + num_to_read = ACL_READ_BUF_SIZE; + + /* Read in more data. */ + ACL_INPUT( (&acl_current_buffer->acl_ch_buf[number_to_move]), + acl_n_chars, num_to_read ); + } + + if ( acl_n_chars == 0 ) + { + if ( number_to_move == ACL_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + aclrestart( aclin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + acl_current_buffer->acl_buffer_status = + ACL_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + acl_n_chars += number_to_move; + acl_current_buffer->acl_ch_buf[acl_n_chars] = ACL_END_OF_BUFFER_CHAR; + acl_current_buffer->acl_ch_buf[acl_n_chars + 1] = ACL_END_OF_BUFFER_CHAR; + + acltext_ptr = &acl_current_buffer->acl_ch_buf[0]; + + return ret_val; + } + + +/* acl_get_previous_state - get the state just before the EOB char was reached */ + +static acl_state_type acl_get_previous_state() + { + register acl_state_type acl_current_state; + register char *acl_cp; + + acl_current_state = acl_start; + + for ( acl_cp = acltext_ptr + ACL_MORE_ADJ; acl_cp < acl_c_buf_p; ++acl_cp ) + { + register ACL_CHAR acl_c = (*acl_cp ? acl_ec[ACL_SC_TO_UI(*acl_cp)] : 1); + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + } + + return acl_current_state; + } + + +/* acl_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = acl_try_NUL_trans( current_state ); + */ + +#ifdef ACL_USE_PROTOS +static acl_state_type acl_try_NUL_trans( acl_state_type acl_current_state ) +#else +static acl_state_type acl_try_NUL_trans( acl_current_state ) +acl_state_type acl_current_state; +#endif + { + register int acl_is_jam; + register char *acl_cp = acl_c_buf_p; + + register ACL_CHAR acl_c = 1; + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + acl_is_jam = (acl_current_state == 103); + + return acl_is_jam ? 0 : acl_current_state; + } + + +#ifndef ACL_NO_UNPUT +#ifdef ACL_USE_PROTOS +static void aclunput( int c, register char *acl_bp ) +#else +static void aclunput( c, acl_bp ) +int c; +register char *acl_bp; +#endif + { + register char *acl_cp = acl_c_buf_p; + + /* undo effects of setting up acltext */ + *acl_cp = acl_hold_char; + + if ( acl_cp < acl_current_buffer->acl_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = acl_n_chars + 2; + register char *dest = &acl_current_buffer->acl_ch_buf[ + acl_current_buffer->acl_buf_size + 2]; + register char *source = + &acl_current_buffer->acl_ch_buf[number_to_move]; + + while ( source > acl_current_buffer->acl_ch_buf ) + *--dest = *--source; + + acl_cp += (int) (dest - source); + acl_bp += (int) (dest - source); + acl_n_chars = acl_current_buffer->acl_buf_size; + + if ( acl_cp < acl_current_buffer->acl_ch_buf + 2 ) + ACL_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--acl_cp = (char) c; + + + acltext_ptr = acl_bp; + acl_hold_char = *acl_cp; + acl_c_buf_p = acl_cp; + } +#endif /* ifndef ACL_NO_UNPUT */ + + +#ifdef __cplusplus +static int aclinput() +#else +static int input() +#endif + { + int c; + + *acl_c_buf_p = acl_hold_char; + + if ( *acl_c_buf_p == ACL_END_OF_BUFFER_CHAR ) + { + /* acl_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( acl_c_buf_p < &acl_current_buffer->acl_ch_buf[acl_n_chars] ) + /* This was really a NUL. */ + *acl_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = acl_c_buf_p - acltext_ptr; + ++acl_c_buf_p; + + switch ( acl_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( aclwrap() ) + { + acl_c_buf_p = acltext_ptr + offset; + return EOF; + } + + if ( ! acl_did_buffer_switch_on_eof ) + ACL_NEW_FILE; +#ifdef __cplusplus + return aclinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + acl_c_buf_p = acltext_ptr + offset; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + ACL_FATAL_ERROR( + "unexpected last match in aclinput()" ); +#else + ACL_FATAL_ERROR( + "unexpected last match in input()" ); +#endif + } + } + } + + c = *(unsigned char *) acl_c_buf_p; /* cast for 8-bit char's */ + *acl_c_buf_p = '\0'; /* preserve acltext */ + acl_hold_char = *++acl_c_buf_p; + + + return c; + } + + +#ifdef ACL_USE_PROTOS +void aclrestart( FILE *input_file ) +#else +void aclrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! acl_current_buffer ) + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); + + acl_init_buffer( acl_current_buffer, input_file ); + acl_load_buffer_state(); + } + + +#ifdef ACL_USE_PROTOS +void acl_switch_to_buffer( ACL_BUFFER_STATE new_buffer ) +#else +void acl_switch_to_buffer( new_buffer ) +ACL_BUFFER_STATE new_buffer; +#endif + { + if ( acl_current_buffer == new_buffer ) + return; + + if ( acl_current_buffer ) + { + /* Flush out information for old buffer. */ + *acl_c_buf_p = acl_hold_char; + acl_current_buffer->acl_buf_pos = acl_c_buf_p; + acl_current_buffer->acl_n_chars = acl_n_chars; + } + + acl_current_buffer = new_buffer; + acl_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (aclwrap()) processing, but the only time this flag + * is looked at is after aclwrap() is called, so it's safe + * to go ahead and always set it. + */ + acl_did_buffer_switch_on_eof = 1; + } + + +#ifdef ACL_USE_PROTOS +void acl_load_buffer_state( void ) +#else +void acl_load_buffer_state() +#endif + { + acl_n_chars = acl_current_buffer->acl_n_chars; + acltext_ptr = acl_c_buf_p = acl_current_buffer->acl_buf_pos; + aclin = acl_current_buffer->acl_input_file; + acl_hold_char = *acl_c_buf_p; + } + + +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_create_buffer( FILE *file, int size ) +#else +ACL_BUFFER_STATE acl_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + ACL_BUFFER_STATE b; + + b = (ACL_BUFFER_STATE) ACL_FLEX_ALLOC( sizeof( struct acl_buffer_state ) ); + if ( ! b ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_create_buffer()" ); + + b->acl_buf_size = size; + + /* acl_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->acl_ch_buf = (char *) ACL_FLEX_ALLOC( b->acl_buf_size + 2 ); + if ( ! b->acl_ch_buf ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_create_buffer()" ); + + b->acl_is_our_buffer = 1; + + acl_init_buffer( b, file ); + + return b; + } + + +#ifdef ACL_USE_PROTOS +void acl_delete_buffer( ACL_BUFFER_STATE b ) +#else +void acl_delete_buffer( b ) +ACL_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == acl_current_buffer ) + acl_current_buffer = (ACL_BUFFER_STATE) 0; + + if ( b->acl_is_our_buffer ) + ACL_FLEX_FREE( (void *) b->acl_ch_buf ); + + ACL_FLEX_FREE( (void *) b ); + } + + +#ifndef ACL_ALWAYS_INTERACTIVE +#ifndef ACL_NEVER_INTERACTIVE +extern int isatty ACL_PROTO(( int )); +#endif +#endif + +#ifdef ACL_USE_PROTOS +void acl_init_buffer( ACL_BUFFER_STATE b, FILE *file ) +#else +void acl_init_buffer( b, file ) +ACL_BUFFER_STATE b; +FILE *file; +#endif + + + { + acl_flush_buffer( b ); + + b->acl_input_file = file; + b->acl_fill_buffer = 1; + +#if ACL_ALWAYS_INTERACTIVE + b->acl_is_interactive = 1; +#else +#if ACL_NEVER_INTERACTIVE + b->acl_is_interactive = 0; +#else + b->acl_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef ACL_USE_PROTOS +void acl_flush_buffer( ACL_BUFFER_STATE b ) +#else +void acl_flush_buffer( b ) +ACL_BUFFER_STATE b; +#endif + + { + b->acl_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->acl_ch_buf[0] = ACL_END_OF_BUFFER_CHAR; + b->acl_ch_buf[1] = ACL_END_OF_BUFFER_CHAR; + + b->acl_buf_pos = &b->acl_ch_buf[0]; + + b->acl_at_bol = 1; + b->acl_buffer_status = ACL_BUFFER_NEW; + + if ( b == acl_current_buffer ) + acl_load_buffer_state(); + } + + +#ifndef ACL_NO_SCAN_BUFFER +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_buffer( char *base, acl_size_t size ) +#else +ACL_BUFFER_STATE acl_scan_buffer( base, size ) +char *base; +acl_size_t size; +#endif + { + ACL_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != ACL_END_OF_BUFFER_CHAR || + base[size-1] != ACL_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (ACL_BUFFER_STATE) ACL_FLEX_ALLOC( sizeof( struct acl_buffer_state ) ); + if ( ! b ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_scan_buffer()" ); + + b->acl_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->acl_buf_pos = b->acl_ch_buf = base; + b->acl_is_our_buffer = 0; + b->acl_input_file = 0; + b->acl_n_chars = b->acl_buf_size; + b->acl_is_interactive = 0; + b->acl_at_bol = 1; + b->acl_fill_buffer = 0; + b->acl_buffer_status = ACL_BUFFER_NEW; + + acl_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef ACL_NO_SCAN_STRING +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_string( aclconst char *str ) +#else +ACL_BUFFER_STATE acl_scan_string( str ) +aclconst char *str; +#endif + { + int len; + for ( len = 0; str[len]; ++len ) + ; + + return acl_scan_bytes( str, len ); + } +#endif + + +#ifndef ACL_NO_SCAN_BYTES +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_bytes( aclconst char *bytes, int len ) +#else +ACL_BUFFER_STATE acl_scan_bytes( bytes, len ) +aclconst char *bytes; +int len; +#endif + { + ACL_BUFFER_STATE b; + char *buf; + acl_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) ACL_FLEX_ALLOC( n ); + if ( ! buf ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = ACL_END_OF_BUFFER_CHAR; + + b = acl_scan_buffer( buf, n ); + if ( ! b ) + ACL_FATAL_ERROR( "bad buffer in acl_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->acl_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef ACL_NO_PUSH_STATE +#ifdef ACL_USE_PROTOS +static void acl_push_state( int new_state ) +#else +static void acl_push_state( new_state ) +int new_state; +#endif + { + if ( acl_start_stack_ptr >= acl_start_stack_depth ) + { + acl_size_t new_size; + + acl_start_stack_depth += ACL_START_STACK_INCR; + new_size = acl_start_stack_depth * sizeof( int ); + + if ( ! acl_start_stack ) + acl_start_stack = (int *) ACL_FLEX_ALLOC( new_size ); + + else + acl_start_stack = (int *) ACL_FLEX_REALLOC( + (void *) acl_start_stack, new_size ); + + if ( ! acl_start_stack ) + ACL_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + acl_start_stack[acl_start_stack_ptr++] = ACL_START; + + BEGIN(new_state); + } +#endif + + +#ifndef ACL_NO_POP_STATE +static void acl_pop_state() + { + if ( --acl_start_stack_ptr < 0 ) + ACL_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(acl_start_stack[acl_start_stack_ptr]); + } +#endif + + +#ifndef ACL_NO_TOP_STATE +static int acl_top_state() + { + return acl_start_stack[acl_start_stack_ptr - 1]; + } +#endif + +#ifndef ACL_EXIT_FAILURE +#define ACL_EXIT_FAILURE 2 +#endif + +#ifdef ACL_USE_PROTOS +static void acl_fatal_error( aclconst char msg[] ) +#else +static void acl_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( ACL_EXIT_FAILURE ); + } + + + +/* Redefine aclless() so it works in section 3 code. */ + +#undef aclless +#define aclless(n) \ + do \ + { \ + /* Undo effects of setting up acltext. */ \ + acltext[aclleng] = acl_hold_char; \ + acl_c_buf_p = acltext + n; \ + acl_hold_char = *acl_c_buf_p; \ + *acl_c_buf_p = '\0'; \ + aclleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef acltext_ptr +#ifdef ACL_USE_PROTOS +static void acl_flex_strncpy( char *s1, aclconst char *s2, int n ) +#else +static void acl_flex_strncpy( s1, s2, n ) +char *s1; +aclconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef ACL_NEED_STRLEN +#ifdef ACL_USE_PROTOS +static int acl_flex_strlen( aclconst char *s ) +#else +static int acl_flex_strlen( s ) +aclconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef ACL_USE_PROTOS +static void *ACL_FLEX_ALLOC( acl_size_t size ) +#else +static void *ACL_FLEX_ALLOC( size ) +acl_size_t size; +#endif + { + return (void *) PERM_MALLOC( size ); + } + +#ifdef ACL_USE_PROTOS +static void *ACL_FLEX_REALLOC( void *ptr, acl_size_t size ) +#else +static void *ACL_FLEX_REALLOC( ptr, size ) +void *ptr; +acl_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) PERM_REALLOC( (char *) ptr, size ); + } + +#ifdef ACL_USE_PROTOS +static void ACL_FLEX_FREE( void *ptr ) +#else +static void ACL_FLEX_FREE( ptr ) +void *ptr; +#endif + { + PERM_FREE( ptr ); + } + +#if ACL_MAIN +int main() + { + acllex(); + return 0; + } +#endif +#line 235 "aclscan.l" + + +void +acl_detab(char *t, char *s) +{ + int ii; + int pos; + int len; + + if (s == NULL || t == NULL) + return; + + len = strlen(s); + pos = 0; + for (ii = 0; ii < len; ii++) { + if (s[ii] == '\t') { + t[pos] = ' '; + } else { + t[pos] = s[ii]; + } + pos++; + } + t[pos] = '\0'; + return; +} + +void +aclerror(const char *s) +{ +char errorStr[256]; + +#if defined(UTEST) || defined(ACL_COMPILER) + printf("ACL file: %s\n", acl_filename); + printf("Syntax error at line: %d, token: %s\n", acl_lineno, acltext); + if ( last_string ) + PERM_FREE(last_string); +#else + sprintf(errorStr, "%d", acl_lineno); + if (acltext) { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 3, acl_filename, errorStr, acltext); + } else { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 2, acl_filename, errorStr); + } + if ( last_string ) + PERM_FREE(last_string); +#endif + +} + +int +acl_InitScanner(NSErr_t *errp, char *filename, char *buffer) +{ + acl_errp = errp; + acl_lineno = 1; + acl_use_buffer = (filename == NULL) ? 1 : 0 ; + if ( filename != NULL ) { + strcpy(acl_filename, filename); +#ifdef UTEST + aclin = fopen(filename, "r"); + if ( aclin == NULL ) { + return(-1); + } +#else + acl_prfd = system_fopenRO(filename); + if ( acl_prfd == NULL ) { + return(-1); + } + aclin = (FILE *) acl_prfd; +#endif + aclrestart(aclin); + } else if ( buffer != NULL ) { + strcpy(acl_filename, "internal-buffer"); + acl_buffer_offset = 0; + acl_buffer_length = strlen(buffer); + acl_buffer = PERM_STRDUP(buffer); + if (acl_buffer == NULL) + return(-1); + aclrestart(NULL); + } else { + return(-1); + } + return(0); +} + +int +acl_EndScanner() +{ + acl_lineno = 0; + if ( acl_use_buffer) { + if ( acl_buffer ) + PERM_FREE(acl_buffer); + } else if ( aclin ) { +#ifdef UTEST + fclose(aclin); +#else + if ( acl_prfd ) { + system_fclose(acl_prfd); + acl_prfd = NULL; + } +#endif + aclin = NULL ; + } + return(0); +} + +int +aclwrap() +{ + return(1); +} + +static int +acl_scanner_input(char *buffer, int max_size) +{ + int len = 0; + + if ( acl_use_buffer ) { + len = (acl_buffer_length > max_size) ? max_size : + acl_buffer_length; + memcpy(buffer, (const void *) &acl_buffer[acl_buffer_offset], + len); + acl_buffer_length -= len; + acl_buffer_offset += len; + } +#ifdef UTEST + else if ( ((len = fread( buffer, 1, max_size, aclin )) == 0) + && ferror( aclin ) ) { + aclerror( "scanner input failed" ); + } +#else + else if ( (len = system_fread( acl_prfd, buffer, max_size)) < 0 ) { + aclerror( "scanner input failed" ); + } +#endif + + return(len); +} diff --git a/lib/libaccess/aclbuild.cpp b/lib/libaccess/aclbuild.cpp new file mode 100644 index 00000000..8c4647dc --- /dev/null +++ b/lib/libaccess/aclbuild.cpp @@ -0,0 +1,1360 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (aclbuild.c) + * + * This module provides functions for building Access Control List + * (ACL) structures in memory. + * + */ + +#include <assert.h> +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/nsauth.h" +#include "libaccess/nsuser.h" +#include "libaccess/nsgroup.h" +#include "libaccess/nsadb.h" +#include "libaccess/aclerror.h" +#include "libaccess/aclstruct.h" +#include "libaccess/aclbuild.h" +#include "libaccess/aclparse.h" +#include "libaccess/acleval.h" +#include "libaccess/usi.h" + +char * ACL_Program = "NSACL"; /* ACL facility name */ + +/* + * Description (accCreate) + * + * This function creates a new access control context, which + * provides context information for a set of ACL definitions. + * The caller also provides a handle for a symbol table to be + * used to store definitions of ACL and rights names. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * stp - symbol table handle (may be null) + * pacc - pointer to returned context handle + * + * Returns: + * + * If the context is created successfully, the return value is zero. + * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + */ + +int accCreate(NSErr_t * errp, void * stp, ACContext_t **pacc) +{ + ACContext_t * acc; /* pointer to new context */ + int rv; /* result value */ + int eid; /* error id */ + + *pacc = 0; + + /* Do we need to create a symbol table? */ + if (stp == 0) { + + /* Yes, create a symbol table for ACL, rights, etc. names */ + rv = symTableNew(&stp); + if (rv < 0) goto err_nomem1; + } + + /* Allocate the context structure */ + acc = (ACContext_t *)MALLOC(sizeof(ACContext_t)); + if (acc == 0) goto err_nomem2; + + /* Initialize it */ + acc->acc_stp = stp; + acc->acc_acls = 0; + acc->acc_rights = 0; + acc->acc_refcnt = 0; + + *pacc = acc; + return 0; + + err_nomem1: + rv = ACLERRNOMEM; + eid = ACLERR3000; + goto err_ret; + + err_nomem2: + rv = ACLERRNOMEM; + eid = ACLERR3020; + + err_ret: + nserrGenerate(errp, rv, eid, ACL_Program, 0); + return rv; +} + +/* + * Description (accDestroy) + * + * This function destroys a set of ACL data structures referenced + * by a specified ACContext_t structure, including the ACContext_t + * itself. + * + * Arguments: + * + * acc - pointer to ACContext_t structure + * flags - bit flags (unused - must be zero) + */ + +void accDestroy(ACContext_t * acc, int flags) +{ + ACL_t * acl; + + if (acc != 0) { + + /* + * First destroy all ACLs and any unnamed structures they reference. + * Note that aclDestroy() modifies the acc_acls list. + */ + while ((acl = acc->acc_acls) != 0) { + + aclDelete(acl); + } + + /* If there's a symbol table, destroy everything it references */ + if (acc->acc_stp != 0) { + symTableEnumerate(acc->acc_stp, 0, accDestroySym); + + /* Destroy the symbol table itself */ + symTableDestroy(acc->acc_stp, 0); + } + + /* Free the ACContext_t structure */ + FREE(acc); + } +} + +/* + * Description (accDestroySym) + * + * This function is called to destroy the data structure associated + * with a specified Symbol_t symbol table entry. It examines the + * type of the symbol and calls the appropriate destructor. + * + * Arguments: + * + * sym - pointer to symbol table entry + * argp - unused - must be zero + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +int accDestroySym(Symbol_t * sym, void * argp) +{ + switch (sym->sym_type) { + case ACLSYMACL: /* ACL */ + aclDestroy((ACL_t *)sym); + break; + + case ACLSYMRIGHT: /* access right */ + { + RightDef_t * rdp = (RightDef_t *)sym; + + if (rdp->rd_sym.sym_name != 0) { + FREE(rdp->rd_sym.sym_name); + } + FREE(rdp); + } + break; + + case ACLSYMRDEF: /* access rights list */ + aclRightSpecDestroy((RightSpec_t *)sym); + break; + + case ACLSYMREALM: /* realm name */ + aclRealmSpecDestroy((RealmSpec_t *)sym); + break; + + case ACLSYMHOST: /* host specifications */ + aclHostSpecDestroy((HostSpec_t *)sym); + break; + + case ACLSYMUSER: /* user/group list */ + aclUserSpecDestroy((UserSpec_t *)sym); + break; + } + + return SYMENUMREMOVE; +} + +/* + * Description (accReadFile) + * + * This function reads a specfied file containing ACL definitions + * and creates data structures in memory to represent the ACLs. + * The caller may provide a pointer to an existing ACContext_t + * structure which will serve as the root of the ACL structures, + * or else a new one will be created. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * aclfile - pointer to the ACL filename string + * pacc - value/result ACContext_t + * + * Returns: + * + * If the ACL file is read successfully, the return value is zero. + * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + */ + +int accReadFile(NSErr_t * errp, char * aclfile, ACContext_t **pacc) +{ + ACContext_t * acc = *pacc; /* pointer to ACL root structure */ + ACLFile_t * acf = 0; /* pointer to ACL file handle */ + void * stp = 0; /* ACL symbol table handle */ + int rv; /* result value */ + int eid; /* error id value */ + + /* Initialize the ACL parser */ + rv = aclParseInit(); + if (rv < 0) goto err_init; + + /* Do we need to create a new ACContext_t structure? */ + if (acc == 0) { + + /* Yes, create a symbol table for ACL, rights, etc. names */ + rv = symTableNew(&stp); + if (rv < 0) goto err_crsym; + + /* Create a root structure for the ACLs, including the symbol table */ + rv = accCreate(errp, stp, &acc); + if (rv < 0) goto err_ret2; + } + + /* Open the ACL definition file */ + rv = aclFileOpen(errp, aclfile, 0, &acf); + if (rv < 0) goto err_ret3; + + /* Parse the ACL definitions, building ACL structures in memory */ + rv = aclACLParse(errp, acf, acc, 0); + if (rv < 0) goto err_ret4; + + aclFileClose(acf, 0); + + if (pacc) *pacc = acc; + + return rv; + + err_init: + eid = ACLERR3100; + goto err_ret; + + err_crsym: + eid = ACLERR3120; + rv = ACLERRNOMEM; + goto err_ret; + + err_ret4: + aclFileClose(acf, 0); + err_ret3: + /* Destroy the ACContext_t if we just created it */ + if (acc != *pacc) { + accDestroy(acc, 0); + } + goto err_ret; + + err_ret2: + symTableDestroy(stp, 0); + + err_ret: + return rv; +} + +/* + * Description (aclAuthDNSAdd) + * + * This function adds a DNS name specification to the DNS filter + * associated with a given host list. The DNS name specification is + * either a fully-qualified domain name or a domain name suffix, + * indicated by a leading ".", e.g. (".mcom.com"). The name + * components included in a suffix must be complete. For example, + * ".scape.com" will not match names ending in ".netscape.com". + * + * Arguments: + * + * hspp - pointer to host list pointer + * dnsspec - DNS name or suffix string pointer + * fqdn - non-zero if dnsspec is fully qualified + * + * Returns: + * + * If successful, the return code is zero. + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h). + */ + +int aclAuthDNSAdd(HostSpec_t **hspp, char * dnsspec, int fqdn) +{ + HostSpec_t * hsp; /* host list pointer */ + void * table; /* access control hash table pointer */ + Symbol_t * sym; /* hash table entry pointer */ + int rv; /* result value */ + + fqdn = (fqdn) ? 1 : 0; + + /* Create the HostSpec_t if it doesn't exist */ + hsp = *hspp; + if (hsp == 0) { + + hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t)); + if (hsp == 0) goto err_nomem; + memset((void *)hsp, 0, sizeof(HostSpec_t)); + hsp->hs_sym.sym_type = ACLSYMHOST; + } + + /* Get pointer to hash table used for DNS filter */ + table = hsp->hs_host.inh_dnf.dnf_hash; + if (table == 0) { + + /* None there yet, so create one */ + rv = symTableNew(&table); + if (rv < 0) goto punt; + hsp->hs_host.inh_dnf.dnf_hash = table; + } + + /* See if the DNS spec is already in the table */ + rv = symTableFindSym(table, dnsspec, fqdn, (void **)&sym); + if (rv < 0) { + if (rv != SYMERRNOSYM) goto punt; + + /* It's not there, so add it */ + sym = (Symbol_t *)MALLOC(sizeof(Symbol_t)); + sym->sym_name = STRDUP(dnsspec); + sym->sym_type = fqdn; + + rv = symTableAddSym(table, sym, (void *)sym); + if (rv < 0) goto err_nomem; + } + + *hspp = hsp; + + punt: + return rv; + + err_nomem: + rv = ACLERRNOMEM; + goto punt; +} + +/* + * Description (aclAuthIPAdd) + * + * This function adds an IP address specification to the IP filter + * associated with a given host list. The IP address specification + * consists of an IP host or network address and an IP netmask. + * For host addresses the netmask value is 255.255.255.255. + * + * Arguments: + * + * hspp - pointer to host list pointer + * ipaddr - IP host or network address + * netmask - IP netmask value + * + * Returns: + * + * If successful, the return code is zero. + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h). + */ + +int aclAuthIPAdd(HostSpec_t **hspp, IPAddr_t ipaddr, IPAddr_t netmask) +{ + HostSpec_t * hsp; /* host list pointer */ + IPFilter_t * ipf; /* IP filter pointer */ + IPNode_t * ipn; /* current node pointer */ + IPNode_t * lastipn; /* last (lower) node pointer */ + IPLeaf_t * leaf; /* leaf node pointer */ + IPAddr_t bitmask; /* bit mask for current node */ + int lastbit; /* number of last bit set in netmask */ + int i; /* loop index */ + + /* Create the HostSpec_t if it doesn't exist */ + hsp = *hspp; + if (hsp == 0) { + + hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t)); + if (hsp == 0) goto err_nomem; + memset((void *)hsp, 0, sizeof(HostSpec_t)); + hsp->hs_sym.sym_type = ACLSYMHOST; + } + + ipf = &hsp->hs_host.inh_ipf; + + /* If the filter doesn't have a root node yet, create it */ + if (ipf->ipf_tree == 0) { + + /* Allocate node */ + ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t)); + if (ipn == 0) goto err_nomem; + + /* Initialize it to test bit 31, but without any descendants */ + ipn->ipn_type = IPN_NODE; + ipn->ipn_bit = 31; + ipn->ipn_parent = NULL; + ipn->ipn_clear = NULL; + ipn->ipn_set = NULL; + ipn->ipn_masked = NULL; + + /* Set it as the root node in the radix tree */ + ipf->ipf_tree = ipn; + } + + /* First we search the tree to see where this IP specification fits */ + + lastipn = NULL; + + for (ipn = ipf->ipf_tree; (ipn != NULL) && (ipn->ipn_type == IPN_NODE); ) { + + /* Get a mask for the bit this node tests */ + bitmask = (IPAddr_t) 1<<ipn->ipn_bit; + + /* Save pointer to last internal node */ + lastipn = ipn; + + /* Is this a bit we care about? */ + if (bitmask & netmask) { + + /* Yes, get address of set or clear descendant pointer */ + ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear; + } + else { + /* No, get the address of the masked descendant pointer */ + ipn = ipn->ipn_masked; + } + } + + /* Did we end up at a leaf node? */ + if (ipn == NULL) { + + /* + * No, well, we need to find a leaf node if possible. The + * reason is that we need an IP address and netmask to compare + * to the IP address and netmask we're inserting. We know that + * they're the same up to the bit tested by the lastipn node, + * but we need to know the *highest* order bit that's different. + * Any leaf node below lastipn will do. + */ + + leaf = NULL; + ipn = lastipn; + + while (ipn != NULL) { + + /* Look for any non-null child link of the current node */ + for (i = 0; i < IPN_NLINKS; ++i) { + if (ipn->ipn_links[i]) break; + } + + /* + * Fail search for leaf if no non-null child link found. + * This should only happen on the root node of the tree + * when the tree is empty. + */ + if (i >= IPN_NLINKS) { + assert(ipn == ipf->ipf_tree); + break; + } + + /* Step to the child node */ + ipn = ipn->ipn_links[i]; + + /* Is it a leaf? */ + if (ipn->ipn_type == IPN_LEAF) { + + /* Yes, search is over */ + leaf = (IPLeaf_t *)ipn; + ipn = NULL; + break; + } + } + } + else { + + /* Yes, loop terminated on a leaf node */ + assert(ipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)ipn; + } + + /* Got a leaf yet? */ + if (leaf != NULL) { + + /* Combine the IP address and netmask differences */ + bitmask = (leaf->ipl_ipaddr ^ ipaddr) | (leaf->ipl_netmask ^ netmask); + + /* Are both the IP address and the netmask the same? */ + if (bitmask == 0) { + + /* Yes, duplicate entry */ + return 0; + } + + /* Find the bit number of the first different bit */ + for (lastbit = 31; + (bitmask & 0x80000000) == 0; --lastbit, bitmask <<= 1) ; + + /* Generate a bit mask with just that bit */ + bitmask = (IPAddr_t) (1 << lastbit); + + /* + * Go up the tree from lastipn, looking for an internal node + * that tests lastbit. Stop if we get to a node that tests + * a higher bit number first. + */ + for (ipn = lastipn, lastipn = (IPNode_t *)leaf; + ipn != NULL; ipn = ipn->ipn_parent) { + + if (ipn->ipn_bit >= lastbit) { + if (ipn->ipn_bit == lastbit) { + /* Need to add a leaf off ipn node */ + lastipn = NULL; + } + break; + } + lastipn = ipn; + } + + assert(ipn != NULL); + } + else { + + /* Just hang a leaf off the lastipn node if no leaf */ + ipn = lastipn; + lastipn = NULL; + lastbit = ipn->ipn_bit; + } + + /* + * If lastipn is not NULL at this point, the new leaf will hang + * off an internal node inserted between the upper node, referenced + * by ipn, and the lower node, referenced by lastipn. The lower + * node may be an internal node or a leaf. + */ + if (lastipn != NULL) { + IPNode_t * parent = ipn; /* parent of the new node */ + + assert((lastipn->ipn_type == IPN_LEAF) || + (ipn == lastipn->ipn_parent)); + + /* Allocate space for the internal node */ + ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t)); + if (ipn == NULL) goto err_nomem; + + ipn->ipn_type = IPN_NODE; + ipn->ipn_bit = lastbit; + ipn->ipn_parent = parent; + ipn->ipn_clear = NULL; + ipn->ipn_set = NULL; + ipn->ipn_masked = NULL; + + bitmask = (IPAddr_t) (1 << lastbit); + + /* + * The values in the leaf we found above determine which + * descendant link of the new internal node will reference + * the subtree that we just ascended. + */ + if (leaf->ipl_netmask & bitmask) { + if (leaf->ipl_ipaddr & bitmask) { + ipn->ipn_set = lastipn; + } + else { + ipn->ipn_clear = lastipn; + } + } + else { + ipn->ipn_masked = lastipn; + } + + /* Allocate space for the new leaf */ + leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t)); + if (leaf == NULL) { + FREE((void *)ipn); + goto err_nomem; + } + + /* Insert internal node in tree */ + + /* First the downward link from the parent to the new node */ + for (i = 0; i < IPN_NLINKS; ++i) { + if (parent->ipn_links[i] == lastipn) { + parent->ipn_links[i] = ipn; + break; + } + } + + /* Then the upward link from the child (if it's not a leaf) */ + if (lastipn->ipn_type == IPN_NODE) { + lastipn->ipn_parent = ipn; + } + } + else { + /* Allocate space for a leaf node only */ + leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t)); + if (leaf == NULL) goto err_nomem; + } + + /* Initialize the new leaf */ + leaf->ipl_type = IPN_LEAF; + leaf->ipl_ipaddr = ipaddr; + leaf->ipl_netmask = netmask; + + /* + * Select the appropriate descendant link of the internal node + * and point it at the new leaf. + */ + bitmask = (IPAddr_t) (1 << ipn->ipn_bit); + if (bitmask & netmask) { + if (bitmask & ipaddr) { + assert(ipn->ipn_set == NULL); + ipn->ipn_set = (IPNode_t *)leaf; + } + else { + assert(ipn->ipn_clear == NULL); + ipn->ipn_clear = (IPNode_t *)leaf; + } + } + else { + assert(ipn->ipn_masked == NULL); + ipn->ipn_masked = (IPNode_t *)leaf; + } + + *hspp = hsp; + + /* Successful completion */ + return 0; + + err_nomem: + return ACLERRNOMEM; +} + +/* + * Description (aclAuthNameAdd) + * + * This function adds a user or group to a given user list, + * in the context of a specified ACL that is being created. The + * name of the user or group is provided by the caller, and is + * looked up in the authentication database associated with the + * specified user list. The return value indicates whether the name + * matched a user or group name, and whether the corresponding user + * or group id was already present in the given user list. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * usp - pointer to user list specification + * rlm - pointer to current authentication realm + * name - pointer to user or group name string + * + * Returns: + * + * The return value is zero if the name is not found in the + * authentication database. If the name is found, the return value + * is a positive value containing bit flags: + * + * AIF_GROUP - name matches a group name + * AIF_USER - name matches a user name + * AIF_DUP - name was already represented in the + * specified user list + * + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h), and an error frame will be generated if + * an error list is provided. + */ + +int aclAuthNameAdd(NSErr_t * errp, UserSpec_t * usp, + Realm_t * rlm, char * name) +{ + void * guoptr; /* group or user object pointer */ + int irv; /* insert result value */ + int eid; /* error id */ + int rv; /* result value */ + + /* There must be a realm specified in order to handle users */ + if (rlm == 0) goto err_norealm; + + /* Open the authentication database if it's not already */ + if (rlm->rlm_authdb == 0) { + + if (rlm->rlm_aif == 0) { + rlm->rlm_aif = &NSADB_AuthIF; + } + + rv = (*rlm->rlm_aif->aif_open)(errp, + rlm->rlm_dbname, 0, &rlm->rlm_authdb); + if (rv < 0) goto err_open; + } + + /* Look up the name in the authentication DB */ + rv = (*rlm->rlm_aif->aif_findname)(errp, rlm->rlm_authdb, name, + (AIF_USER|AIF_GROUP), (void **)&guoptr); + if (rv <= 0) { + if (rv < 0) goto err_adb; + + /* The name was not found in the database */ + return 0; + } + + /* The name was found. Was it a user name? */ + if (rv == AIF_USER) { + + /* Yes, add the user id to the user list */ + irv = usiInsert(&usp->us_user.uu_user, ((UserObj_t *)guoptr)->uo_uid); + rv = ANA_USER; + } + else { + + /* No, must be a group name. Add group id to an_groups list. */ + irv = usiInsert(&usp->us_user.uu_group, + ((GroupObj_t *)guoptr)->go_gid); + rv = ANA_GROUP; + } + + /* Examine the result of the insert operation */ + if (irv <= 0) { + if (irv < 0) goto err_ins; + + /* Id was already in the list */ + rv |= ANA_DUP; + } + + punt: + return rv; + + err_norealm: + eid = ACLERR3400; + rv = ACLERRNORLM; + nserrGenerate(errp, rv, eid, ACL_Program, 1, name); + goto punt; + + err_open: + eid = ACLERR3420; + rv = ACLERROPEN; + nserrGenerate(errp, rv, eid, ACL_Program, + 2, rlm->rlm_dbname, system_errmsg()); + goto punt; + + err_adb: + /* Error accessing authentication database. */ + eid = ACLERR3440; + rv = ACLERRADB; + nserrGenerate(errp, rv, eid, ACL_Program, 2, rlm->rlm_dbname, name); + goto punt; + + err_ins: + /* Error on insert operation. Must be lack of memory. */ + eid = ACLERR3460; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; +} + +/* + * Description (aclClientsDirCreate) + * + * This function allocates and initializes a new ACClients_t + * ACL directive. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, a pointer to the new ACClients_t is returned. + * A shortage of dynamic memory is indicated by a null return value. + */ + +ACClients_t * aclClientsDirCreate() +{ + ACClients_t * acd; /* pointer to new ACClients_t */ + + acd = (ACClients_t *)MALLOC(sizeof(ACClients_t)); + if (acd != 0) { + memset((void *)acd, 0, sizeof(ACClients_t)); + } + + return acd; +} + +/* + * Description (aclCreate) + * + * This function creates a new ACL root structure. The caller + * specifies the name to be associated with the ACL. The ACL handle + * returned by this function is passed to other functions in this + * module when adding information to the ACL. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acc - pointer to an access control context + * aclname - pointer to ACL name string + * pacl - pointer to returned ACL handle + * + * Returns: + * + * The return value is zero if the ACL is created successfully. + * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + */ + +int aclCreate(NSErr_t * errp, ACContext_t * acc, char * aclname, ACL_t **pacl) +{ + ACL_t * acl; /* pointer to created ACL */ + int rv; /* result value */ + int eid; /* error id */ + + *pacl = 0; + + /* Allocate the ACL_t structure */ + acl = (ACL_t *) MALLOC(sizeof(ACL_t)); + if (acl == 0) goto err_nomem; + + /* Initialize the structure */ + memset((void *)acl, 0, sizeof(ACL_t)); + acl->acl_sym.sym_name = STRDUP(aclname); + acl->acl_sym.sym_type = ACLSYMACL; + acl->acl_acc = acc; + acl->acl_refcnt = 1; + + /* Add it to the symbol table for the specified context */ + rv = symTableAddSym(acc->acc_stp, &acl->acl_sym, (void *)acl); + if (rv < 0) goto err_addsym; + + /* Add it to the list of ACLs for the specified context */ + acl->acl_next = acc->acc_acls; + acc->acc_acls = acl; + acc->acc_refcnt += 1; + + *pacl = acl; + return 0; + + err_nomem: + rv = ACLERRNOMEM; + eid = ACLERR3200; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto done; + + err_addsym: + FREE(acl); + rv = ACLERRDUPSYM; + eid = ACLERR3220; + nserrGenerate(errp, rv, eid, ACL_Program, 1, aclname); + + done: + return rv; +} + +/* + * Description (aclDestroy) + * + * This function destroys an ACL structure and its sub-structures. + * It does not free the ACContext_t referenced by the ACL. + * + * Arguments: + * + * acl - pointer to ACL_t structure + */ + +void aclDestroy(ACL_t * acl) +{ + ACL_t **pacl; /* ACL list link pointer */ + ACDirective_t * acd; /* ACL directive pointer */ + ACDirective_t * nacd; /* next ACL directive pointer */ + + /* Is there an ACContext_t structure? */ + if (acl->acl_acc != 0) { + + /* Remove this ACL from the list in the ACContext_t structure */ + for (pacl = &acl->acl_acc->acc_acls; + *pacl != 0; pacl = &(*pacl)->acl_next) { + + if (*pacl == acl) { + *pacl = acl->acl_next; + acl->acl_acc->acc_refcnt -= 1; + break; + } + } + } + + /* Destroy each ACL directive */ + for (acd = acl->acl_dirf; acd != 0; acd = nacd) { + nacd = acd->acd_next; + aclDirectiveDestroy(acd); + } + + /* Free the ACL rights list if it is unnamed */ + if ((acl->acl_rights != 0) && (acl->acl_rights->rs_sym.sym_name == 0)) { + aclRightSpecDestroy(acl->acl_rights); + } + + /* Free the ACL name string, if any */ + if (acl->acl_sym.sym_name != 0) { + FREE(acl->acl_sym.sym_name); + } + + /* Free the ACL itself */ + FREE(acl); +} + +/* + * Description (aclDelete) + * + * This function removes a specified ACL from the symbol table + * associated with its ACL context, and then destroys the ACL + * structure and any unnamed objects it references (other than + * the ACL context). + * + * Arguments: + * + * acl - pointer to the ACL + */ + +void aclDelete(ACL_t * acl) +{ + ACContext_t * acc = acl->acl_acc; + + if ((acc != 0) && (acl->acl_sym.sym_name != 0)) { + symTableRemoveSym(acc->acc_stp, &acl->acl_sym); + } + + aclDestroy(acl); +} + +/* + * Description (aclDirectiveAdd) + * + * This function adds a given directive to a specified ACL. + * + * Arguments: + * + * acl - pointer to the ACL + * acd - pointer to the directive to be added + * + * Returns: + * + * If successful, the return value is zero. An error is indicated + * by a negative return value. + */ + +int aclDirectiveAdd(ACL_t * acl, ACDirective_t * acd) +{ + /* Add the directive to the end of the ACL's directive list */ + acd->acd_next = 0; + + if (acl->acl_dirl == 0) { + /* First entry in empty list */ + acl->acl_dirf = acd; + } + else { + /* Append to end of list */ + acl->acl_dirl->acd_next = acd; + } + + acl->acl_dirl = acd; + + return 0; +} + +/* + * Description (aclDirectiveCreate) + * + * This function allocates and initializes a new ACDirective_t + * structure, representing an ACL directive. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, the return value is a pointer to a new ACDirective_t. + * Otherwise the return value is null. + */ + +ACDirective_t * aclDirectiveCreate() +{ + ACDirective_t * acd; + + acd = (ACDirective_t *) MALLOC(sizeof(ACDirective_t)); + if (acd != 0) { + memset((void *)acd, 0, sizeof(ACDirective_t)); + } + + return acd; +} + +/* + * Description (aclDirectiveDestroy) + * + * This function destroys an ACL directive structure. + * + * Arguments: + * + * acd - pointer to ACL directive structure + */ + +void aclDirectiveDestroy(ACDirective_t * acd) +{ + switch (acd->acd_action) { + case ACD_ALLOW: + case ACD_DENY: + { + ACClients_t * acp; + ACClients_t * nacp; + + /* Free a list of ACClients_t structures */ + for (acp = acd->acd_cl; acp != 0; acp = nacp) { + nacp = acp->cl_next; + + /* Free the HostSpec_t if it's there and unnamed */ + if ((acp->cl_host != 0) && + (acp->cl_host->hs_sym.sym_name == 0)) { + aclHostSpecDestroy(acp->cl_host); + } + + /* Free the UserSpec_t if it's there and unnamed */ + if ((acp->cl_user != 0) && + (acp->cl_user->us_sym.sym_name == 0)) { + aclUserSpecDestroy(acp->cl_user); + } + } + } + break; + + case ACD_AUTH: + { + RealmSpec_t * rsp = acd->acd_auth.au_realm; + + /* Destroy the RealmSpec_t if it's unnamed */ + if ((rsp != 0) && (rsp->rs_sym.sym_name == 0)) { + aclRealmSpecDestroy(rsp); + } + } + break; + } + + FREE(acd); +} + +/* + * Description (aclDNSSpecDestroy) + * + * This function destroys an entry in a DNS filter. It is intended + * mainly to be used by aclHostSpecDestroy(). + * + * Arguments: + * + * sym - pointer to Symbol_t for DNS filter entry + * argp - unused (must be zero) + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +int aclDNSSpecDestroy(Symbol_t * sym, void * argp) +{ + if (sym != 0) { + + /* Free the DNS specification string if any */ + if (sym->sym_name != 0) { + FREE(sym->sym_name); + } + + /* Free the Symbol_t structure */ + FREE(sym); + } + + /* Indicate that the symbol table entry should be removed */ + return SYMENUMREMOVE; +} + +/* + * Description (aclHostSpecDestroy) + * + * This function destroys a HostSpec_t structure and its sub-structures. + * + * Arguments: + * + * hsp - pointer to HostSpec_t structure + */ + +void aclHostSpecDestroy(HostSpec_t * hsp) +{ + if (hsp == 0) return; + + /* Free the IP filter if any */ + if (hsp->hs_host.inh_ipf.ipf_tree != 0) { + IPNode_t * ipn; /* current node pointer */ + IPNode_t * parent; /* parent node pointer */ + int i; + + /* Traverse tree, freeing nodes */ + for (parent = hsp->hs_host.inh_ipf.ipf_tree; parent != NULL; ) { + + /* Look for a link to a child node */ + for (i = 0; i < IPN_NLINKS; ++i) { + ipn = parent->ipn_links[i]; + if (ipn != NULL) break; + } + + /* Any children for the parent node? */ + if (ipn == NULL) { + + /* Otherwise back up the tree */ + ipn = parent; + parent = ipn->ipn_parent; + + /* Free the lower node */ + FREE(ipn); + continue; + } + + /* + * Found a child node for the current parent. + * NULL out the downward link and check it out. + */ + parent->ipn_links[i] = NULL; + + /* Is it a leaf? */ + if (ipn->ipn_type == IPN_LEAF) { + /* Yes, free it */ + FREE(ipn); + continue; + } + + /* No, step down the tree */ + parent = ipn; + } + } + + /* Free the DNS filter if any */ + if (hsp->hs_host.inh_dnf.dnf_hash != 0) { + + /* Destroy each entry in the symbol table */ + symTableEnumerate(hsp->hs_host.inh_dnf.dnf_hash, 0, + aclDNSSpecDestroy); + + /* Destroy the symbol table itself */ + symTableDestroy(hsp->hs_host.inh_dnf.dnf_hash, 0); + } + + /* Free the symbol name if any */ + if (hsp->hs_sym.sym_name != 0) { + FREE(hsp->hs_sym.sym_name); + } + + /* Free the HostSpec_t structure */ + FREE(hsp); +} + +/* + * Description (aclRealmSpecDestroy) + * + * This function destroys a RealmSpec_t structure. + * + * Arguments: + * + * rsp - pointer to RealmSpec_t structure + */ + +void aclRealmSpecDestroy(RealmSpec_t * rsp) +{ + /* Close the realm authentication database if it appears open */ + if ((rsp->rs_realm.rlm_aif != 0) && + (rsp->rs_realm.rlm_authdb != 0)) { + (*rsp->rs_realm.rlm_aif->aif_close)(rsp->rs_realm.rlm_authdb, 0); + } + + /* Free the prompt string if any */ + if (rsp->rs_realm.rlm_prompt != 0) { + FREE(rsp->rs_realm.rlm_prompt); + } + + /* Free the database filename string if any */ + if (rsp->rs_realm.rlm_dbname != 0) { + FREE(rsp->rs_realm.rlm_dbname); + } + + /* Free the realm specification name if any */ + if (rsp->rs_sym.sym_name != 0) { + FREE(rsp->rs_sym.sym_name); + } + + /* Free the RealmSpec_t structure */ + FREE(rsp); +} + +/* + * Description (aclRightDef) + * + * This function find or creates an access right with a specified + * name in a given ACL context. If a new access right definition + * is created, it assigns a unique integer identifier to the the + * right, adds it to the ACL context symbol table and to the + * list of all access rights for the context. Note that access + * right names are case-insensitive. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acc - pointer to an access control context + * rname - access right name (e.g. "GET") + * prd - pointer to returned RightDef_t pointer + * (may be null) + * + * Returns: + * + * The return value is zero if the access right definition already + * existed or one if it was created successfully. Otherwise it is + * a negative error code (ACLERRxxxx - see aclerror.h), and an error + * frame will be generated if an error list is provided. + */ + +int aclRightDef(NSErr_t * errp, + ACContext_t * acc, char * rname, RightDef_t **prd) +{ + RightDef_t * rdp; /* pointer to right definition */ + int eid; /* error id code */ + int rv; /* result value */ + static int last_rid = 0; /* last assigned right id */ + + /* See if there's already a symbol table entry for it */ + rv = symTableFindSym(acc->acc_stp, rname, ACLSYMRIGHT, (void **)&rdp); + if (rv) { + + /* No, create an entry */ + + /* Allocate a right definition structure and initialize it */ + rdp = (RightDef_t *)MALLOC(sizeof(RightDef_t)); + if (rdp == 0) goto err_nomem; + + rdp->rd_sym.sym_name = STRDUP(rname); + rdp->rd_sym.sym_type = ACLSYMRIGHT; + rdp->rd_next = acc->acc_rights; + rdp->rd_id = ++last_rid; + + /* Add the right name to the symbol table for the ACL context */ + rv = symTableAddSym(acc->acc_stp, &rdp->rd_sym, (void *)rdp); + if (rv) goto err_stadd; + + /* Add the right definition to the list for the ACL context */ + acc->acc_rights = rdp; + + /* Indicate a new right definition was created */ + rv = 1; + } + + /* Return a pointer to the RightDef_t structure if indicated */ + if (prd != 0) *prd = rdp; + + return rv; + + err_nomem: + eid = ACLERR3600; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; + + err_stadd: + FREE(rdp->rd_sym.sym_name); + FREE(rdp); + eid = ACLERR3620; + rv = ACLERRDUPSYM; + nserrGenerate(errp, rv, eid, ACL_Program, 1, rname); + + punt: + return rv; +} + +/* + * Description (aclRightSpecDestroy) + * + * This function destroys a RightSpec_t structure. + * + * Arguments: + * + * rsp - pointer to RightSpec_t structure + */ + +void aclRightSpecDestroy(RightSpec_t * rsp) +{ + if (rsp != 0) { + + UILFREE(&rsp->rs_list); + + if (rsp->rs_sym.sym_name != 0) { + FREE(rsp->rs_sym.sym_name); + } + + FREE(rsp); + } +} + +/* + * Description (aclUserSpecCreate) + * + * This function allocates and initializes a new UserSpec_t + * structure, representing a list of users and groups. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, the return value is a pointer to a new UserSpec_t. + * Otherwise the return value is null. + */ + +UserSpec_t * aclUserSpecCreate() +{ + UserSpec_t * usp; + + usp = (UserSpec_t *) MALLOC(sizeof(UserSpec_t)); + if (usp != 0) { + memset((void *)usp, 0, sizeof(UserSpec_t)); + usp->us_sym.sym_type = ACLSYMUSER; + } + + return usp; +} + +/* + * Description (aclUserSpecDestroy) + * + * This function destroys a UserSpec_t structure. + * + * Arguments: + * + * usp - pointer to UserSpec_t structure + */ + +void aclUserSpecDestroy(UserSpec_t * usp) +{ + if (usp != 0) { + + UILFREE(&usp->us_user.uu_user); + UILFREE(&usp->us_user.uu_group); + + if (usp->us_sym.sym_name != 0) { + FREE(usp->us_sym.sym_name); + } + + FREE(usp); + } +} 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(); +} diff --git a/lib/libaccess/aclcache.h b/lib/libaccess/aclcache.h new file mode 100644 index 00000000..1f375bcf --- /dev/null +++ b/lib/libaccess/aclcache.h @@ -0,0 +1,27 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef CACHE_H +#define CACHE_H + +NSPR_BEGIN_EXTERN_C + +extern void ACL_ListHashInit(void); +extern void ACL_ListHashUpdate(ACLListHandle_t **acllistp); +extern void ACL_Destroy(void); +extern int ACL_CritHeld(void); +extern void ACL_CritInit(void); +extern void ACL_UriHashInit(void); +extern void ACL_UriHashDestroy(void); +extern int ACL_CacheCheck(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheEnter(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheAbort(ACLListHandle_t **acllist_p); +extern void ACL_Init2(void); +extern int ACL_RegisterInit (); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/aclerror.cpp b/lib/libaccess/aclerror.cpp new file mode 100644 index 00000000..2cbf2874 --- /dev/null +++ b/lib/libaccess/aclerror.cpp @@ -0,0 +1,246 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (aclerror.c) + * + * This module provides error-handling facilities for ACL-related + * errors. + */ + +#include "base/systems.h" +#ifdef NSPR20 +#include "prprf.h" +#else +#include "nspr/prprf.h" +#endif +#include <base/nsassert.h> +#include "libaccess/nserror.h" +#include "libaccess/nsautherr.h" +#include "libaccess/aclerror.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define aclerrnomem XP_GetAdminStr(DBT_AclerrfmtAclerrnomem) +#define aclerropen XP_GetAdminStr(DBT_AclerrfmtAclerropen) +#define aclerrdupsym1 XP_GetAdminStr(DBT_AclerrfmtAclerrdupsym1) +#define aclerrdupsym3 XP_GetAdminStr(DBT_AclerrfmtAclerrdupsym3) +#define aclerrsyntax XP_GetAdminStr(DBT_AclerrfmtAclerrsyntax) +#define aclerrundef XP_GetAdminStr(DBT_AclerrfmtAclerrundef) +#define aclaclundef XP_GetAdminStr(DBT_AclerrfmtAclaclundef) +#define aclerradb XP_GetAdminStr(DBT_AclerrfmtAclerradb) +#define aclerrparse1 XP_GetAdminStr(DBT_AclerrfmtAclerrparse1) +#define aclerrparse2 XP_GetAdminStr(DBT_AclerrfmtAclerrparse2) +#define aclerrparse3 XP_GetAdminStr(DBT_AclerrfmtAclerrparse3) +#define aclerrnorlm XP_GetAdminStr(DBT_AclerrfmtAclerrnorlm) +#define unknownerr XP_GetAdminStr(DBT_AclerrfmtUnknownerr) +#define aclerrinternal XP_GetAdminStr(DBT_AclerrfmtAclerrinternal) +#define aclerrinval XP_GetAdminStr(DBT_AclerrfmtAclerrinval) +#define aclerrfail XP_GetAdminStr(DBT_AclerrfmtAclerrfail) +#define aclerrio XP_GetAdminStr(DBT_AclerrfmtAclerrio) + +/* + * Description (aclErrorFmt) + * + * This function formats an ACL error message into a buffer provided + * by the caller. The ACL error information is passed in an error + * list structure. The caller can indicate how many error frames + * should be processed. A newline is inserted between messages for + * different error frames. The error frames on the error list are + * all freed, regardless of the maximum depth for traceback. + * + * Arguments: + * + * errp - error frame list pointer + * msgbuf - pointer to error message buffer + * maxlen - maximum length of generated message + * maxdepth - maximum depth for traceback + */ + +void aclErrorFmt(NSErr_t * errp, char * msgbuf, int maxlen, int maxdepth) +{ + NSEFrame_t * efp; /* error frame pointer */ + int len; /* length of error message text */ + int depth = 0; /* current depth */ + + msgbuf[0] = 0; + + while ((efp = errp->err_first) != 0) { + + /* Stop if the message buffer is full */ + if (maxlen <= 0) break; + + if (depth > 0) { + /* Put a newline & tab between error frame messages */ + *msgbuf++ = '\n'; + if (--maxlen <= 0) break; + *msgbuf++ = '\t'; + if (--maxlen <= 0) break; + } + + if (!strcmp(efp->ef_program, ACL_Program)) { + + /* Identify the facility generating the error and the id number */ + len = PR_snprintf(msgbuf, maxlen, + "[%s%d] ", efp->ef_program, efp->ef_errorid); + msgbuf += len; + maxlen -= len; + + if (maxlen <= 0) break; + + len = 0; + + switch (efp->ef_retcode) { + + case ACLERRFAIL: + case ACLERRNOMEM: + case ACLERRINTERNAL: + case ACLERRINVAL: + switch (efp->ef_errc) { + case 3: + PR_snprintf(msgbuf, maxlen, efp->ef_errv[0], efp->ef_errv[1], efp->ef_errv[2]); + break; + case 2: + PR_snprintf(msgbuf, maxlen, efp->ef_errv[0], efp->ef_errv[1]); + break; + case 1: + strncpy(msgbuf, efp->ef_errv[0], maxlen); + break; + default: + NS_ASSERT(0); /* don't break -- continue into case 0 */ + case 0: + switch (efp->ef_retcode) { + case ACLERRFAIL: + strncpy(msgbuf, XP_GetAdminStr(DBT_AclerrfmtAclerrfail), maxlen); + break; + case ACLERRNOMEM: + strncpy(msgbuf, aclerrnomem, maxlen); + break; + case ACLERRINTERNAL: + strncpy(msgbuf, aclerrinternal, maxlen); + break; + case ACLERRINVAL: + strncpy(msgbuf, aclerrinval, maxlen); + break; + } + break; + } + msgbuf[maxlen-1] = '\0'; + len = strlen(msgbuf); + break; + + case ACLERROPEN: + /* File open error: filename, system_errmsg */ + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, aclerropen, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRDUPSYM: + /* Duplicate symbol */ + if (efp->ef_errc == 1) { + /* Duplicate symbol: filename, line#, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrdupsym1, + efp->ef_errv[0]); + } + else if (efp->ef_errc == 3) { + /* Duplicate symbol: symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrdupsym3, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + break; + + case ACLERRSYNTAX: + if (efp->ef_errc == 2) { + /* Syntax error: filename, line# */ + len = PR_snprintf(msgbuf, maxlen, aclerrsyntax, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRUNDEF: + if (efp->ef_errorid == ACLERR3800) { + /* Undefined symbol: acl, method/database name */ + len = PR_snprintf(msgbuf, maxlen, aclaclundef, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + else if (efp->ef_errc == 3) { + /* Undefined symbol: filename, line#, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrundef, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + break; + + case ACLERRADB: + if (efp->ef_errc == 2) { + /* Authentication database error: DB name, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerradb, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRPARSE: + if (efp->ef_errc == 2) { + /* Parse error: filename, line# */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse2, + efp->ef_errv[0], efp->ef_errv[1]); + } + else if (efp->ef_errc == 3) { + /* Parse error: filename, line#, token */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse3, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + else if (efp->ef_errc == 1) { + /* Parse error: line or pointer */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse1, + efp->ef_errv[0]); + } + break; + + case ACLERRNORLM: + if (efp->ef_errc == 1) { + /* No realm: name */ + len = PR_snprintf(msgbuf, maxlen, aclerrnorlm, + efp->ef_errv[0]); + } + break; + + case ACLERRIO: + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, aclerrio, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + default: + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + break; + } + } + else if (!strcmp(efp->ef_program, NSAuth_Program)) { + nsadbErrorFmt(errp, msgbuf, maxlen, maxdepth - depth); + } + else { + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + } + + msgbuf += len; + maxlen -= len; + + /* Free this frame */ + nserrFFree(errp, efp); + + if (++depth >= maxdepth) break; + } + + /* Free any remaining error frames */ + nserrDispose(errp); +} diff --git a/lib/libaccess/acleval.cpp b/lib/libaccess/acleval.cpp new file mode 100644 index 00000000..a2be1f7b --- /dev/null +++ b/lib/libaccess/acleval.cpp @@ -0,0 +1,556 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (acleval.c) + * + * This module provides functions for evaluating Access Control List + * (ACL) structures in memory. + * + */ + +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/symbols.h" +#include "libaccess/aclerror.h" +#include "libaccess/acleval.h" +#include <assert.h> + +/* + * Description (RLMEQUIV) + * + * Macro for realm comparison. Both realm pointers must be non-null. + * The realms are equivalent if the pointers are equal, or if the + * authentication methods and database names are the same. The + * prompt string is not considered. + */ +#define RLMEQUIV(rlm1, rlm2) (((rlm1) != 0) && ((rlm2) != 0) && \ + (((rlm1) == (rlm2)) || \ + (((rlm1)->rlm_ameth == (rlm2)->rlm_ameth) && \ + ((rlm1)->rlm_dbname != 0) && \ + ((rlm2)->rlm_dbname != 0) && \ + !strcmp((rlm1)->rlm_dbname, \ + (rlm2)->rlm_dbname)))) + +int aclDNSLookup(DNSFilter_t * dnf, char * dnsspec, int fqdn, char **match) +{ + char * subdns; /* suffix of client DNS name */ + void * table; /* hash table pointer */ + Symbol_t * sym; /* DNS spec symbol pointer */ + int rv; /* result value */ + + fqdn = (fqdn) ? 1 : 0; + + if (match) *match = 0; + + /* Handle null or empty filter */ + if ((dnf == 0) || (dnf->dnf_hash == 0)) { + + return ACL_NOMATCH; + } + + /* Got the client's DNS name? */ + if (!dnsspec || !*dnsspec) { + /* No, use special one */ + dnsspec = "unknown"; + } + + /* Get hash table pointer */ + table = dnf->dnf_hash; + + /* + * Look up each possible suffix for the client domain name, + * starting with the entire string, and working toward the + * last component. + */ + + subdns = dnsspec; + + while (subdns != 0) { + + /* Look up the domain name suffix in the hash table */ + rv = symTableFindSym(table, subdns, fqdn, (void **)&sym); + if (rv == 0) break; + + /* Step to the next level */ + if (subdns[0] == '.') subdns += 1; + subdns = strchr(subdns, '.'); + + /* If it was fully qualified, now it's not */ + fqdn = 0; + } + + /* One more possibility if nothing found yet... */ + if (rv) { + rv = symTableFindSym(table, "*", 0, (void **)&sym); + } + + if (rv == 0) { + if (match) *match = sym->sym_name; + rv = ACL_DNMATCH; + } + else rv = ACL_NOMATCH; + + return rv; +} + +int aclIPLookup(IPFilter_t * ipf, IPAddr_t ipaddr, void **match) +{ + IPLeaf_t * leaf; /* radix tree leaf pointer */ + IPAddr_t bitmask; /* bit mask for current node */ + IPNode_t * ipn; /* current internal node */ + IPNode_t * lastipn; /* last internal node seen in search */ + IPNode_t * mipn; /* ipn_masked subtree root pointer */ + + if (match) *match = 0; + + /* Handle null or empty IP filter */ + if ((ipf == 0) || (ipf->ipf_tree == 0)) goto fail; + + lastipn = NULL; + ipn = ipf->ipf_tree; + + /* + * The tree traversal first works down the tree, under the assumption + * that all of the bits in the given IP address may be significant. + * The internal nodes of the tree will cause particular bits of the + * IP address to be tested, and the ipn_clear or ipn_set link to + * a descendant followed accordingly. The internal nodes are arranged + * in such a way that high-order bits are tested before low-order bits. + * Usually some bits are skipped, as they are not needed to distinguish + * the entries in the tree. + * + * At the bottom of the tree, a leaf node may be found, or the last + * descendant link may be NULL. If a leaf node is found, it is + * tested for a match against the given IP address. If it doesn't + * match, or the link was NULL, backtracking begins, as described + * below. + * + * Backtracking follows the ipn_parent links back up the tree from + * the last internal node, looking for internal nodes with ipn_masked + * descendants. The subtrees attached to these links are traversed + * downward, as before, with the same processing at the bottom as + * the first downward traversal. Following the ipn_masked links is + * essentially examining the possibility that the IP address bit + * associated with the internal node may be masked out by the + * ipl_netmask in a leaf at the bottom of such a subtree. Since + * the ipn_masked links are examined from the bottom of the tree + * to the top, this looks at the low-order bits first. + */ + + while (ipn != NULL) { + + /* + * Work down the tree testing bits in the IP address indicated + * by the internal nodes. Exit the loop when there are no more + * internal nodes. + */ + while ((ipn != NULL) && (ipn->ipn_type == IPN_NODE)) { + + /* Save pointer to internal node */ + lastipn = ipn; + + /* Get a mask for the bit this node tests */ + bitmask = (IPAddr_t) 1<<ipn->ipn_bit; + + /* Select link to follow for this IP address */ + ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear; + } + + /* Did we end up with a non-NULL node pointer? */ + if (ipn != NULL) { + + /* It must be a leaf node */ + assert(ipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)ipn; + + /* Is it a matching leaf? */ + if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win; + } + + /* + * Backtrack, starting at lastipn. Search each subtree + * emanating from an ipn_masked link. Step up the tree + * until the ipn_masked link of the node referenced by + * "ipf->ipf_tree" has been considered. + */ + + for (ipn = lastipn; ipn != NULL; ipn = ipn->ipn_parent) { + + /* + * Look for a node with a non-NULL masked link, but don't + * go back to the node we just came from. + */ + + if ((ipn->ipn_masked != NULL) && (ipn->ipn_masked != lastipn)) { + + /* Get the root of this subtree */ + mipn = ipn->ipn_masked; + + /* If this is an internal node, start downward traversal */ + if (mipn->ipn_type == IPN_NODE) { + ipn = mipn; + break; + } + + /* Otherwise it's a leaf */ + assert(mipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)mipn; + + /* Is it a matching leaf? */ + if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win; + } + + /* Don't consider nodes above the given root */ + if (ipn == ipf->ipf_tree) goto fail; + + lastipn = ipn; + } + } + + fail: + /* No matching entry found */ + return ACL_NOMATCH; + + win: + /* Found a match in leaf */ + if (match) *match = (void *)leaf; + + return ACL_IPMATCH; +} + +int aclUserLookup(UidUser_t * uup, UserObj_t * uoptr) +{ + int gl1cnt; /* elements left in uup->uu_group list */ + int gl2cnt; /* elements left in uoptr->uo_groups list */ + USI_t * gl1ptr; /* pointer to next group in uup->uu_group */ + USI_t * gl2ptr; /* pointer to next group in uoptr->uo_groups */ + + /* Try for a direct match on the user id */ + if (usiPresent(&uup->uu_user, uoptr->uo_uid)) { + return ACL_USMATCH; + } + + /* + * Now we want to see if there are any matches between the + * uup->uu_group group id list and the list of groups in the + * user object. + */ + + gl1cnt = UILCOUNT(&uup->uu_group); + gl1ptr = UILLIST(&uup->uu_group); + gl2cnt = UILCOUNT(&uoptr->uo_groups); + gl2ptr = UILLIST(&uoptr->uo_groups); + + while ((gl1cnt > 0) && (gl2cnt > 0)) { + + if (*gl1ptr == *gl2ptr) { + return ACL_GRMATCH; + } + + if (*gl1ptr < *gl2ptr) { + ++gl1ptr; + --gl1cnt; + } + else { + ++gl2ptr; + --gl2cnt; + } + } + + return ACL_NOMATCH; +} + +/* + * Description (aclEvaluate) + * + * This function evaluates a given ACL against specified client + * information and a particular access right that is needed to + * service the client. It can optionally return the ACL directive + * number which allows or denies the client's access. + * + * Arguments: + * + * acl - pointer to ACL to evaluate + * arid - desired access right id value + * clauth - pointer to client authentication information + * padn - pointer to returned ACL directive number + * (may be null) + * + * Returns: + * + * A return value of zero indicates that the given ACL does not + * control the desired access right, or contains no directives which + * match the specified client. A positive return value contains a + * value of ACD_ALLOW, ACD_DENY, or ACD_AUTH, and may also have the + * ACD_ALWAYS bit flag set. The value indicates whether the client + * should be allowed or denied access, or whether authentication is + * needed. The ACD_ALWAYS flag indicates if the action should occur + * immediately, terminating any further ACL evaluation. An error + * is indicated by a negative error code (ACLERRxxxx - see aclerror.h). + */ + +int aclEvaluate(ACL_t * acl, USI_t arid, ClAuth_t * clauth, int * padn) +{ + ACDirective_t * acd; /* current ACL directive pointer */ + RightSpec_t * rsp; /* pointer to rights controlled by ACL */ + ACClients_t * csp; /* pointer to clients specification */ + HostSpec_t * hsp; /* pointer to host specification */ + UserSpec_t * usp; /* pointer to user specification */ + Realm_t * rlm = 0; /* current authentication realm pointer */ + Realm_t * authrlm = 0; /* realm to be used for authentication */ + int ndir; /* ACL directive number */ + int rv; /* result value */ + int decision = 0; /* current access control decision */ + int result = 0; /* function return value */ + int mdn = 0; /* matching directive number */ + + if (padn) *padn = 0; + + /* Does this ACL control the desired access right? */ + + rsp = acl->acl_rights; + if ((rsp == 0) || !usiPresent(&rsp->rs_list, arid)) { + + /* No, nothing to do */ + return 0; + } + + ndir = 0; + + /* Loop on each ACL directive */ + for (acd = acl->acl_dirf; acd != 0; acd = acd->acd_next) { + + /* Bump directive number */ + ++ndir; + + /* Dispatch on directive action code */ + switch (acd->acd_action) { + + case ACD_ALLOW: + case ACD_DENY: + + /* Loop to process list of client specifications */ + for (csp = acd->acd_cl; csp != 0; csp = csp->cl_next) { + + /* Is there a host list? */ + hsp = csp->cl_host; + if (hsp != 0) { + + /* An empty host list will not match */ + rv = 0; + + /* Yes, is there an IP address filter? */ + if (hsp->hs_host.inh_ipf.ipf_tree != 0) { + + /* + * Yes, see if the the client's IP address + * matches anything in the IP filter. + */ + rv = aclIPLookup(&hsp->hs_host.inh_ipf, + clauth->cla_ipaddr, 0); + } + + /* If no IP match, is there a DNS filter? */ + if (!rv && (hsp->hs_host.inh_dnf.dnf_hash != 0)) { + + /* Yes, try for a DNS match */ + rv = aclDNSLookup(&hsp->hs_host.inh_dnf, + clauth->cla_dns, 1, 0); + } + + /* + * Does the client match the host list? If not, skip + * to the next client specification. + */ + if (!rv) continue; + } + + /* Is there a user list? */ + usp = csp->cl_user; + if (usp != 0) { + + /* Yes, has the client user been authenticated yet? */ + if ((clauth->cla_realm != 0) && (clauth->cla_uoptr != 0)) { + + /* + * Yes, has the client user been authenticated in the + * realm associated with this user list? + */ + if (RLMEQUIV(rlm, clauth->cla_realm)) { + + /* + * Yes, does the user spec allow all + * authenticated users? + */ + rv = (usp->us_flags & ACL_USALL) ? ACL_GRMATCH : 0; + if (!rv) { + + /* + * No, need to check client user against list. + */ + rv = aclUserLookup(&usp->us_user, + clauth->cla_uoptr); + } + + /* Got a match yet? */ + if (rv) { + + /* + * Yes, update the the access control decision, + * clearing any pending authentication request + * flag. + */ + authrlm = 0; + decision = acd->acd_action; + + /* Copy the "always" flag to the result */ + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + } + else { + + /* + * The client has been authenticated already, + * but not in the realm used by this directive. + * Since directives in a given ACL are not + * independent policy statements, it seems that + * the proper thing to do here is to reject + * this ACL in its entirity. This case is not + * an authentication failure per se, but rather + * an inability to evaluate this particular + * ACL directive which requires authentication. + */ + return 0; + } + } + else { + + /* + * The client user has not been authenticated in this + * realm yet, but could potentially be one of the + * users on this user list. This directive is + * therefore "potentially matching". The question + * is: would it change the current decision to allow + * or deny the client if the client user actually did + * match the user list? + */ + if ((authrlm == 0) && (decision != acd->acd_action)) { + + /* + * Yes, set the "request authentication" flag, + * along with ACD_ALWAYS if it is set in the + * directive. + */ + authrlm = rlm; + decision = ACD_AUTH; + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + } + } + else { + + /* + * There is no user list. Therefore any user, + * authenticated or not, is considered a match. + * Update the decision, and clear the + * "authentication requested" flag. + */ + authrlm = 0; + decision = acd->acd_action; + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + + /* + * If we hit a client specification that requires + * immediate action, exit the loop. + */ + if (result & ACD_ALWAYS) break; + } + break; + + case ACD_AUTH: + + /* Got a pointer to a realm specification? */ + if (acd->acd_auth.au_realm != 0) { + + /* Yes, update the current realm pointer */ + rlm = &acd->acd_auth.au_realm->rs_realm; + + /* Has the client already successfully authenticated? */ + if ((clauth->cla_realm == 0) || (clauth->cla_uoptr == 0)) { + + /* + * No, if this is an "always" directive, override any + * previously selected realm and request authentication. + */ + if ((acd->acd_flags & ACD_ALWAYS) != 0) { + + /* Set decision to request authentication */ + authrlm = rlm; + decision = ACD_AUTH; + result = ACD_ALWAYS; + mdn = ndir; + } + } + } + break; + + case ACD_EXEC: + + /* Conditionally terminate ACL evaluation */ + switch (decision) { + case ACD_ALLOW: + if (acd->acd_flags & ACD_EXALLOW) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + case ACD_DENY: + if (acd->acd_flags & ACD_EXDENY) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + case ACD_AUTH: + if (acd->acd_flags & ACD_EXAUTH) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + default: + break; + } + break; + + default: + break; + } + + /* + * If we hit a directive that requires immediate action, exit + * the loop. + */ + if (result & ACD_ALWAYS) break; + } + + out: + /* If the decision is to request authentication, set the desired realm */ + if (decision == ACD_AUTH) { + clauth->cla_realm = authrlm; + } + + /* Combine decision with flags already in result */ + result |= decision; + + /* Return matching directive number if desired */ + if (padn) *padn = mdn; + + return result; +} diff --git a/lib/libaccess/aclflush.cpp b/lib/libaccess/aclflush.cpp new file mode 100644 index 00000000..dfee47d6 --- /dev/null +++ b/lib/libaccess/aclflush.cpp @@ -0,0 +1,178 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Source file for the ACL_CacheFlush-related routines. + */ + +#include <base/nsassert.h> +#include <base/util.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/las.h> +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> + +extern void ACL_DatabaseDestroy(void); + +PRIntn +deletelists(PRHashEntry *he, PRIntn i, void *arg) +{ + ACLListHandle_t *acllist=(ACLListHandle_t *)he->value; + NSErr_t *errp = 0; + + NS_ASSERT(he); + NS_ASSERT(he->value); + + if (acllist->ref_count) { + // If the list is in use, increment the counter. Then set the + // stale flag. The other user can't delete the list since we're + // counted as well. Finally, decrement the counter and whoever + // sets it to zero will delete the ACL List. + NS_ASSERT(ACL_CritHeld()); + acllist->ref_count++; + acllist->flags |= ACL_LIST_STALE; + if (--acllist->ref_count == 0) + ACL_ListDestroy(errp, acllist); + } else { + ACL_ListDestroy(errp, acllist); + } + + return 0; +} + +PRIntn +restartdeletelists(PRHashEntry *he, PRIntn i, void *arg) +{ + NSErr_t *errp = 0; + + // Cannot be anyone left using the lists, so just free them no matter + // what. + ACLListHandle_t *acllist=(ACLListHandle_t *)he->value; + + NS_ASSERT(he); + NS_ASSERT(he->value); + + ACL_ListDestroy(errp, acllist); + + return 0; +} + +static AclCacheFlushFunc_t AclCacheFlushRoutine = NULL; + +NSAPI_PUBLIC int +ACL_CacheFlushRegister(AclCacheFlushFunc_t flush_func) +{ + NS_ASSERT(flush_func); + AclCacheFlushRoutine = flush_func; + + return 0; +} + +NSAPI_PUBLIC int +ACL_CacheFlush(void) +{ + ACLGlobal_p newACLGlobal; + NSErr_t *errp = 0; + + NS_ASSERT(ACLGlobal); + NS_ASSERT(ACLGlobal->masterlist); + NS_ASSERT(ACLGlobal->listhash); + NS_ASSERT(ACLGlobal->urihash); + NS_ASSERT(ACLGlobal->urigethash); + NS_ASSERT(ACLGlobal->pool); + + ACL_CritEnter(); + + // Swap the pointers. Keep using the current database/method tables + // until the new ones are built. This is a kludge. An in-progress + // evaluation could conceivably get messed up, but the window seems + // small. + newACLGlobal = oldACLGlobal; + + oldACLGlobal = ACLGlobal; + ACLGlobal = newACLGlobal; + + // Prepare the new ACLGlobal structure + ACL_UriHashInit(); /* Also initializes ACLGlobal->pool */ + ACL_ListHashInit(); + ACLGlobal->evalhash = oldACLGlobal->evalhash; + ACLGlobal->flushhash = oldACLGlobal->flushhash; + ACLGlobal->methodhash = oldACLGlobal->methodhash; + ACLGlobal->dbtypehash = oldACLGlobal->dbtypehash; + ACLGlobal->dbnamehash = oldACLGlobal->dbnamehash; + ACLGlobal->attrgetterhash = oldACLGlobal->attrgetterhash; + ACLGlobal->databasepool = oldACLGlobal->databasepool; + ACLGlobal->methodpool = oldACLGlobal->methodpool; + + // Mark all existing ACL Lists as stale. Delete any unreferenced ones. + PR_HashTableEnumerateEntries(oldACLGlobal->listhash, deletelists, NULL); + + // Delete the old master list. + ACL_ListDestroy(errp, oldACLGlobal->masterlist); + oldACLGlobal->masterlist = NULL; + PR_HashTableDestroy(oldACLGlobal->listhash); + oldACLGlobal->listhash = NULL; + PR_HashTableDestroy(oldACLGlobal->urihash); + oldACLGlobal->urihash = NULL; + PR_HashTableDestroy(oldACLGlobal->urigethash); + oldACLGlobal->urigethash = NULL; + pool_destroy(oldACLGlobal->pool); + oldACLGlobal->pool = NULL; + memset(oldACLGlobal, 0, sizeof(ACLGlobal_s)); + + + // Read in the ACLs again in lib/frame + if (AclCacheFlushRoutine) { + (*AclCacheFlushRoutine)(); + } + + ACL_CritExit(); + + return 0; +} + + +NSAPI_PUBLIC void +ACL_Restart(void *clntData) +{ + NSErr_t *errp = 0; + + NS_ASSERT(ACLGlobal); + NS_ASSERT(ACLGlobal->masterlist); + NS_ASSERT(ACLGlobal->listhash); + NS_ASSERT(ACLGlobal->urihash); + NS_ASSERT(ACLGlobal->urigethash); + NS_ASSERT(ACLGlobal->pool); + + // Unlike ACL_CacheFlush, this routine can be much more cavalier about + // freeing up memory, since there's guaranteed to be no users about at + // this time. + + ACL_DatabaseDestroy(); + ACL_MethodSetDefault(errp, ACL_METHOD_INVALID); + + // Mark all existing ACL Lists as stale. Delete any unreferenced ones + // (i.e. all of them) + PR_HashTableEnumerateEntries(ACLGlobal->listhash, restartdeletelists, NULL); + + // Delete the master list. + ACL_ListDestroy(errp, ACLGlobal->masterlist); + + ACL_LasHashDestroy(); + PR_HashTableDestroy(ACLGlobal->listhash); + PR_HashTableDestroy(ACLGlobal->urihash); + PR_HashTableDestroy(ACLGlobal->urigethash); + pool_destroy(ACLGlobal->pool); + + PERM_FREE(ACLGlobal); + ACLGlobal = NULL; + PERM_FREE(oldACLGlobal); + oldACLGlobal = NULL; + + return; +} diff --git a/lib/libaccess/aclparse.cpp b/lib/libaccess/aclparse.cpp new file mode 100644 index 00000000..d8c429fe --- /dev/null +++ b/lib/libaccess/aclparse.cpp @@ -0,0 +1,2241 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (aclparse.c) + * + * This module provides functions for parsing a file containing + * Access Control List (ACL) definitions. It builds a representation + * of the ACLs in memory, using the services of the aclbuild module. + */ + +#include <base/systems.h> +#include <base/file.h> +#include <base/util.h> +#include <netsite.h> +#include <libaccess/nsadb.h> +#include <libaccess/aclerror.h> +#include <libaccess/aclparse.h> +#include <libaccess/symbols.h> + +#ifdef XP_UNIX +#include <sys/types.h> +#include <netinet/in.h> /* ntohl */ +#include <arpa/inet.h> +#endif + +void * aclChTab = 0; /* character class table handle */ + +static char * classv[] = { + " \t\r\f\013", /* class 0 - whitespace */ + "\n", /* class 1 - newline */ + ",.;@*()+{}\"\'", /* class 2 - special characters */ + "0123456789", /* class 3 - digits */ + /* class 4 - letters */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "-", /* class 5 - hyphen */ + "_", /* class 6 - underscore */ + "/-_.:" /* class 7 - filename special characters */ +}; + +static int classc = sizeof(classv)/sizeof(char *); + +/* + * Description (aclAuthListParse) + * + * This function parses an auth-list. An auth-list specifies + * combinations of user/group names and host addresses/names. + * An auth-list entry can identify a collection of users and/or + * groups, a collection of hosts by IP addresses or DNS names, + * or a combination of the two. Each auth-spec adds another + * ACClients_t structure to the specified list. + * + * The syntax for an auth-list is: + * + * auth-list ::= auth-spec | auth-list "," auth-spec + * auth-spec ::= auth-users [at-token auth-hosts] + * auth-users - see aclAuthUsersParse() + * auth-hosts - see aclAuthHostsParse() + * at-token ::= "at" | "@" + * + * The caller provides a pointer to a ClientSpec_t structure, + * which is built up with new information as auth-specs are parsed. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acc - pointer to ACL context object + * rlm - pointer to authentication realm object + * clsp - pointer to returned ACClients_t list head + * + * Returns: + * + * If successful, the return value is the token type of the token + * following the auth-list, i.e. the first token which is not + * recognized as the start of an auth-spec. It is the caller's + * responsibility to validate this token as a legitimate terminator + * of an auth-list. If a parsing error occurs in the middle of + * an auth-spec, the return value is ACLERRPARSE, and an error frame + * is generated if an error list is provided. For other kinds of + * errors a negative error code (from aclerror.h) is returned. + */ + +int aclAuthListParse(NSErr_t * errp, ACLFile_t * acf, + ACContext_t * acc, Realm_t * rlm, ACClients_t **clsp) +{ + void * token = acf->acf_token; /* token handle */ + ACClients_t * csp; /* client spec pointer */ + UserSpec_t * usp; /* user spec pointer */ + HostSpec_t * hsp; /* host spec pointer */ + int rv; /* result value */ + int eid; /* error id */ + + /* Loop once for each auth-spec */ + for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) { + + usp = 0; + hsp = 0; + + /* Parse auth-users into user and group lists in the ACClients_t */ + rv = aclAuthUsersParse(errp, acf, rlm, &usp, 0); + if (rv < 0) break; + + /* Is the at-token there? */ + if ((rv == TOKEN_AT) || !strcasecmp(lex_token(token), KEYWORD_AT)) { + + /* Step to the next token after the at-token */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + /* Parse auth-hosts part, adding information to the HostSpec_t */ + rv = aclAuthHostsParse(errp, acf, acc, &hsp); + if (rv < 0) break; + } + + /* Create a new ACClients_t structure for the parsed information */ + csp = (ACClients_t *)MALLOC(sizeof(ACClients_t)); + if (csp == 0) goto err_nomem; + + csp->cl_next = 0; + csp->cl_user = usp; + csp->cl_host = hsp; + + /* Add it to the end of the list referenced by clsp */ + while (*clsp != 0) clsp = &(*clsp)->cl_next; + *clsp = csp; + + /* Need a "," to keep going */ + if (rv != TOKEN_COMMA) break; + } + + return rv; + + err_nomem: + eid = ACLERR1000; + nserrGenerate(errp, ACLERRNOMEM, eid, ACL_Program, 0); + return ACLERRNOMEM; +} + +/* + * Description (aclAuthHostsParse) + * + * This function parses a list of IP address and/or DNS name + * specifications, adding information to the IP and DNS filters + * associated with a specified HostSpec_t. The syntax of the + * auth-hosts construct is: + * + * auth-hosts ::= auth-host-elem | "(" auth-host-list ")" + * | "hosts" host-list-name + * auth-host-elem ::= auth-ip-spec | auth-dns-spec + * auth-ip-spec ::= ipaddr | ipaddr netmask + * auth-dns-spec ::= fqdn | dns-suffix + * auth-host-list ::= auth-host-elem | auth-host-list "," auth-host-elem + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acc - pointer to ACL context object + * hspp - pointer to HostSpec_t pointer + * + * Returns: + * + * If successful, the return value is the token type of the token + * following the auth-hosts, i.e. either the first token after a + * single auth-host-elem or the first token after the closing ")" + * of a list of auth-host-elems. It is the caller's responsibility + * to validate this token as a legitimate successor of auth-hosts. + * If a parsing error occurs in the middle of auth-hosts, the return + * value is ACLERRPARSE, and an error frame is generated if an error + * list is provided. For other kinds of errors a negative error + * code (from aclerror.h) is returned. + */ + +int aclAuthHostsParse(NSErr_t * errp, + ACLFile_t * acf, ACContext_t * acc, HostSpec_t **hspp) +{ + void * token = acf->acf_token; /* token handle */ + char * tokenstr; /* token string pointer */ + int islist = 0; /* true if auth-host-list */ + int fqdn; /* fully qualified domain name */ + IPAddr_t ipaddr; /* IP address value */ + IPAddr_t netmask; /* IP netmask value */ + int arv; /* alternate result value */ + int rv; /* result value */ + int eid; /* error id */ + char linestr[16]; /* line number string buffer */ + + rv = acf->acf_ttype; + + /* Are we starting an auth-host-list? */ + if (rv == TOKEN_LPAREN) { + + /* Yes, it appears so */ + islist = 1; + + /* Step token to first auth-host-elem */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + } + else if (rv == TOKEN_IDENT) { + + /* Could this be "hosts host-list-name"? */ + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_HOSTS)) { + + /* We don't support lists of host lists yet */ + if (*hspp != 0) goto err_unshl; + + /* Get host-list-name */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + + if (rv != TOKEN_IDENT) goto err_hlname; + + tokenstr = lex_token(token); + + /* Look up the host-list-name in the ACL symbol table */ + rv = symTableFindSym(acc->acc_stp, + tokenstr, ACLSYMHOST, (void **)hspp); + if (rv < 0) goto err_undefhl; + + /* Step to token after the host-list-name */ + rv = aclGetToken(errp, acf, 0); + + return rv; + } + } + + /* Loop for each auth-host-elem */ + for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) { + + /* Does this look like an auth-ip-spec? */ + if (rv == TOKEN_NUMBER) { + + /* Yes, go parse it */ + rv = aclGetIPAddr(errp, acf, &ipaddr, &netmask); + if (rv < 0) goto punt; + + arv = aclAuthIPAdd(hspp, ipaddr, netmask); + if (arv < 0) goto err_ipadd; + } + else if ((rv == TOKEN_STAR) || (rv == TOKEN_IDENT)) { + + /* Get fully qualified DNS name indicator value */ + fqdn = (rv == TOKEN_IDENT) ? 1 : 0; + + /* This looks like the start of an auth-dns-spec */ + rv = aclGetDNSString(errp, acf); + if (rv < 0) goto punt; + + tokenstr = lex_token(token); + + /* If the DNS spec begins with "*.", strip the "*" */ + if (tokenstr && (tokenstr[0] == '*') && (tokenstr[1] == '.')) { + tokenstr += 1; + } + + arv = aclAuthDNSAdd(hspp, tokenstr, fqdn); + if (arv < 0) goto err_dnsadd; + + /* Pick up the next token */ + rv = aclGetToken(errp, acf, 0); + } + else break; + + /* If this is a list, we need a "," to keep going */ + if (!islist || (rv != TOKEN_COMMA)) break; + } + + /* Were we parsing an auth-host-list? */ + if (islist) { + + /* Yes, check for closing ")" */ + if (acf->acf_ttype != TOKEN_RPAREN) goto err_norp; + + /* Got it. Step to next token for caller. */ + rv = aclGetToken(errp, acf, 0); + } + + punt: + return rv; + + err_unshl: + eid = ACLERR1100; + goto err_parse; + + err_hlname: + eid = ACLERR1120; + goto err_parse; + + err_undefhl: + eid = ACLERR1140; + rv = ACLERRUNDEF; + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, tokenstr); + goto punt; + + err_ipadd: + eid = ACLERR1180; + rv = arv; + goto err_ret; + + err_dnsadd: + eid = ACLERR1200; + rv = arv; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; + + err_norp: + eid = ACLERR1220; + err_parse: + rv = ACLERRPARSE; + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; +} + +/* + * Description (aclAuthUsersParse) + * + * This function parses a list of users and groups subject to + * authorization, adding the information to a specified UserSpec_t. + * The syntax it parses is: + * + * auth-users ::= auth-user-elem | "(" auth-user-list ")" + * auth-user-elem ::= username | groupname + * | "all" | "anyone" + * auth-user-list ::= auth-user-elem | auth-user-list "," auth-user-elem + * + * If the 'elist' argument is non-null, an auth-user-list will be + * accepted without the enclosing parentheses. Any invalid user + * or group names will not cause a fatal error, but will be returned + * in an array of strings via 'elist'. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * rlm - pointer to authentication realm object + * uspp - pointer to UserSpec_t pointer + * elist - pointer to returned pointer to array + * of strings containing invalid user or + * group names (may be null) + * + * Returns: + * + * If successful, the return value is the token type of the token + * following the auth-users, i.e. either the first token after a + * single auth-user-elem or the first token after the closing ")" + * of a list of auth-user-elems. It is the caller's responsibility + * to validate this token as a legitimate successor of auth-users. + * If a parsing error occurs in the middle of auth-users, the return + * value is ACLERRPARSE, and an error frame is generated if an error + * list is provided. For other kinds of errors a negative error + * code (from aclerror.h) is returned. + */ + +int aclAuthUsersParse(NSErr_t * errp, ACLFile_t * acf, + Realm_t * rlm, UserSpec_t **uspp, char ***elist) +{ + void * token = acf->acf_token; /* token handle */ + char * tokenstr; /* token string pointer */ + UserSpec_t * usp; /* user list head structure */ + int islist = 0; /* true if auth-user-list */ + int inlist = 0; /* true if UserSpec_t was supplied */ + int any = 0; /* true if KEYWORD_ANY seen */ + int all = 0; /* true if KEYWORD_ALL seen */ + int elemcnt = 0; /* count of auth-user-elem seen */ + int elen = 0; /* length of evec in (char *) */ + int ecnt = 0; /* entries used in evec */ + char **evec = 0; /* list of bad user/group names */ + int rv; /* result value */ + int eid; /* error id */ + char linestr[16]; /* line number string buffer */ + int errc = 2; + + usp = *uspp; + if ((usp != 0) && (usp->us_flags & ACL_USALL)) all = 1; + + if (elist != 0) inlist = 1; + else { + + /* Check for opening "(" */ + if (acf->acf_ttype == TOKEN_LPAREN) { + + /* Looks like an auth-user-list */ + islist = 1; + + /* Step token to first auth-user-elem */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + } + } + + /* Loop for each auth-user-elem */ + for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) { + + /* Looking for a user or group identifier */ + if ((rv == TOKEN_IDENT) || (rv == TOKEN_STRING)) { + + /* + * If KEYWORD_ALL or KEYWORD_ANY has already appeared + * in this auth-spec, then return an error. + */ + if (all | any) goto err_allany; + + /* Check for reserved words */ + tokenstr = lex_token(token); + + /* KEYWORD_AT begins auth-hosts, but is invalid here */ + if (!strcasecmp(tokenstr, KEYWORD_AT)) break; + + /* Check for special group names */ + if (!strcasecmp(tokenstr, KEYWORD_ANY)) { + + /* + * Any user, with no authentication needed. This can + * only appear once in an auth-spec, and cannot be used + * in combination with KEYWORD_ALL (or any other user or + * group identifiers, but that will get checked before + * we return). + */ + + if ((elemcnt > 0) || (usp != 0)) goto err_any; + any = 1; + } + else if (!strcasecmp(tokenstr, KEYWORD_ALL)) { + + /* + * Any authenticated user. This can only appear once in + * an auth-spec, and cannot be used in combination with + * KEYWORD_ANY (or any other user or group identifiers, + * but that will get checked before we return). + */ + + if (elemcnt > 0) goto err_all; + + /* Create a UserSpec_t structure if we haven't got one yet */ + if (usp == 0) { + usp = aclUserSpecCreate(); + if (usp == 0) goto err_nomem1; + *uspp = usp; + } + + usp->us_flags |= ACL_USALL; + all = 1; + } + else { + + /* Create a UserSpec_t structure if we haven't got one yet */ + if (usp == 0) { + usp = aclUserSpecCreate(); + if (usp == 0) goto err_nomem2; + *uspp = usp; + } + + /* This should be a user or group name */ + rv = aclAuthNameAdd(errp, usp, rlm, tokenstr); + if (rv <= 0) { + + /* The name was not found in the authentication DB */ + if (elist != 0) { + if (evec == 0) { + evec = (char **)MALLOC(4*sizeof(char *)); + evec[0] = 0; + ecnt = 1; + elen = 4; + } + else if (ecnt >= elen) { + elen += 4; + evec = (char **)REALLOC(evec, elen*sizeof(char *)); + } + evec[ecnt-1] = STRDUP(tokenstr); + evec[ecnt] = 0; + ++ecnt; + + } + else if (rv < 0) goto err_badgun; + } + + /* Don't allow duplicate names */ + if (rv & ANA_DUP) { + if (elist == 0) goto err_dupgun; + } + } + + /* Count number of auth-user-elems seen */ + elemcnt += 1; + + /* Get the token after the auth-user-elem */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + } + + /* If this is a list, we need a "," to keep going */ + if (!(islist | inlist) || (rv != TOKEN_COMMA)) break; + } + + /* Were we parsing an auth-user-list? */ + if (islist) { + + /* Yes, check for closing ")" */ + if (acf->acf_ttype != TOKEN_RPAREN) goto err_norp; + + /* Got it. Step to next token for caller. */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + } + + /* + * If we didn't see any auth-user-elems, then the auth-user we were + * called to parse is missing. We will forgive and forget if the + * current token is a comma, however, so as to allow empty auth-specs. + */ + if ((elemcnt <= 0) && (rv != TOKEN_COMMA)) { + goto err_noelem; + } + + punt: + /* Return list of bad names if indicated */ + if (elist != 0) *elist = evec; + + return rv; + + err_badgun: + /* Encountered an unknown user or group name */ + eid = ACLERR1360; + rv = ACLERRUNDEF; + goto err_retgun; + + err_dupgun: + /* A user or group name was specified multiple times */ + eid = ACLERR1380; + rv = ACLERRDUPSYM; + goto err_retgun; + + err_retgun: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, tokenstr); + goto punt; + + err_norp: + /* Missing ")" */ + eid = ACLERR1400; + goto err_parse; + + err_noelem: + eid = ACLERR1420; + goto err_parse; + + err_all: + eid = ACLERR1440; + goto err_parse; + + err_any: + eid = ACLERR1460; + goto err_parse; + + err_allany: + eid = ACLERR1480; + goto err_parse; + + err_nomem1: + eid = ACLERR1500; + rv = ACLERRNOMEM; + errc = 0; + goto err_ret; + + err_nomem2: + eid = ACLERR1520; + rv = ACLERRNOMEM; + errc = 0; + goto err_ret; + + err_parse: + rv = ACLERRPARSE; + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, errc, acf->acf_filename, linestr); + goto punt; +} + +/* + * Description (aclDirectivesParse) + * + * This function parses the directives inside an ACL definition. + * The syntax for a directive list is: + * + * dir-list ::= directive | dir-list ";" directive + * directive ::= auth-directive | access-directive | exec-directive + * auth-directive ::= dir-force "authenticate" ["in" realm-spec] + * access-directive ::= dir-force dir-access auth-list + * exec-directive ::= dir-force "execute" ["if" exec-optlist] + * exec-optlist ::= exec-condition | exec-optlist "," exec-condition + * exec-condition ::= dir-access | "authenticate" + * dir-force ::= "Always" | "Default" + * dir-access ::= "allow" | "deny" + * + * See aclAuthListParse() for auth-list syntax. + * See aclRealmSpecParse() for realm-spec syntax. + * + * The caller provides a pointer to an ACL structure, which is + * built up with new information as directives are parsed. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acl - pointer to ACL structure + * + * Returns: + * + * If successful, the return value is the token type of the token + * following the directive list, i.e. the first token which is not + * recognized as the start of a directive. It is the caller's + * responsibility to validate this token as a legitimate terminator + * of a directive list. If a parsing error occurs in the middle of + * a directive, the return value is ACLERRPARSE, and an error frame + * is generated if an error list is provided. For other kinds of + * errors a negative error code (from aclerror.h) is returned. + */ + +int aclDirectivesParse(NSErr_t * errp, ACLFile_t * acf, ACL_t * acl) +{ + void * token = acf->acf_token; /* token handle */ + char * tokenstr; /* token string */ + Realm_t * rlm = 0; /* current realm pointer */ + ACDirective_t * acd; /* directive pointer */ + int action; /* directive action code */ + int flags; /* directive action flags */ + int arv; /* alternate return value */ + int rv; /* result value */ + int eid; /* error id */ + char linestr[16]; /* line number string buffer */ + + /* Look for top-level directives */ + for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) { + + action = 0; + flags = 0; + + /* Check for beginning of directive */ + if (rv == TOKEN_IDENT) { + + /* Check identifier for directive dir-force keywords */ + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_DEFAULT)) { + flags = ACD_DEFAULT; + } + else if (!strcasecmp(tokenstr, "always")) { + flags = ACD_ALWAYS; + } + else break; + + /* + * Now we're looking for dir-access, "authenticate", + * or "execute". + */ + rv = aclGetToken(errp, acf, 0); + + /* An identifier would be nice ... */ + if (rv != TOKEN_IDENT) goto err_access; + + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_AUTH)) { + + /* process auth-directive */ + action = ACD_AUTH; + + /* Create a new directive object */ + acd = aclDirectiveCreate(); + if (acd == 0) goto err_nomem1; + + /* Get the next token after KEYWORD_AUTH */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + /* Could we have "in" realm-spec here? */ + if (rv == TOKEN_IDENT) { + + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_IN)) { + + /* Get the next token after KEYWORD_IN */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + /* Parse the realm-spec */ + rv = aclRealmSpecParse(errp, acf, acl->acl_acc, + &acd->acd_auth.au_realm); + if (rv < 0) break; + + /* Set current realm */ + if (acd->acd_auth.au_realm != 0) { + + /* Close database in current realm if any */ + if (rlm && rlm->rlm_authdb) { + (*rlm->rlm_aif->aif_close)(rlm->rlm_authdb, 0); + rlm->rlm_authdb = 0; + } + + rlm = &acd->acd_auth.au_realm->rs_realm; + } + } + } + + /* Add this directive to the ACL */ + acd->acd_action = action; + acd->acd_flags = flags; + + arv = aclDirectiveAdd(acl, acd); + if (arv < 0) goto err_diradd1; + } + else if (!strcasecmp(tokenstr, KEYWORD_EXECUTE)) { + + /* process exec-directive */ + action = ACD_EXEC; + + /* Create a new directive object */ + acd = aclDirectiveCreate(); + if (acd == 0) goto err_nomem3; + + /* Get the next token after KEYWORD_EXECUTE */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + /* Could we have "if" exec-optlist here? */ + if (rv == TOKEN_IDENT) { + + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_IF)) { + + for (;;) { + + /* Get the next token after KEYWORD_IF or "," */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + /* + * Looking for "allow", "deny", or "authenticate" + */ + if (rv == TOKEN_IDENT) { + + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_ALLOW)) { + flags |= ACD_EXALLOW; + } + else if (!strcasecmp(tokenstr, KEYWORD_DENY)) { + flags |= ACD_EXDENY; + } + else if (!strcasecmp(tokenstr, KEYWORD_AUTH)) { + flags |= ACD_EXAUTH; + } + else goto err_exarg; + } + + /* End of directive if no comma */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) break; + + if (rv != TOKEN_COMMA) break; + } + } + } + else flags = (ACD_EXALLOW|ACD_EXDENY|ACD_EXAUTH); + + if (rv < 0) break; + + /* Add this directive to the ACL */ + acd->acd_action = action; + acd->acd_flags = flags; + + arv = aclDirectiveAdd(acl, acd); + if (arv < 0) goto err_diradd3; + } + else { + + /* process access-directive */ + + if (!strcasecmp(tokenstr, KEYWORD_ALLOW)) { + action = ACD_ALLOW; + } + else if (!strcasecmp(tokenstr, KEYWORD_DENY)) { + action = ACD_DENY; + } + else goto err_acctype; + + /* Get the next token after dir-access */ + rv = aclGetToken(errp, acf, 0); + + /* Create a new directive object */ + acd = aclDirectiveCreate(); + if (acd == 0) goto err_nomem2; + + /* Parse a list of auth-specs */ + rv = aclAuthListParse(errp, acf, acl->acl_acc, rlm, + &acd->acd_cl); + if (rv < 0) break; + + /* Add this directive to the ACL */ + acd->acd_action = action; + acd->acd_flags = flags; + + arv = aclDirectiveAdd(acl, acd); + if (arv < 0) goto err_diradd2; + } + } + + /* Need a ";" to keep going */ + if (rv != TOKEN_EOS) break; + } + + punt: + /* Close database in current realm if any */ + if (rlm && rlm->rlm_authdb) { + (*rlm->rlm_aif->aif_close)(rlm->rlm_authdb, 0); + rlm->rlm_authdb = 0; + } + + return rv; + + err_access: + /* dir-access not present */ + eid = ACLERR1600; + rv = ACLERRPARSE; + goto err_ret; + + err_acctype: + /* dir-access identifier is invalid */ + eid = ACLERR1620; + rv = ACLERRPARSE; + goto err_ret; + + err_diradd1: + eid = ACLERR1640; + rv = arv; + tokenstr = 0; + goto err_ret; + + err_diradd2: + eid = ACLERR1650; + rv = arv; + tokenstr = 0; + goto err_ret; + + err_nomem1: + eid = ACLERR1660; + rv = ACLERRNOMEM; + tokenstr = 0; + goto err_ret; + + err_nomem2: + eid = ACLERR1680; + rv = ACLERRNOMEM; + tokenstr = 0; + goto err_ret; + + err_nomem3: + eid = ACLERR1685; + rv = ACLERRNOMEM; + tokenstr = 0; + goto err_ret; + + err_diradd3: + eid = ACLERR1690; + rv = arv; + tokenstr = 0; + goto err_ret; + + err_exarg: + eid = ACLERR1695; + rv = ACLERRSYNTAX; + goto err_ret; + + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + if (tokenstr) { + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, tokenstr); + } + else { + nserrGenerate(errp, rv, eid, ACL_Program, + 2, acf->acf_filename, linestr); + } + goto punt; +} + +/* + * Description (aclACLParse) + * + * This function parses a data stream containing ACL definitions, + * and builds a representation of the ACLs in memory. Each ACL + * has a user-specified name, and a pointer to the ACL structure + * is stored under the name in a symbol table provided by the caller. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acc - pointer to ACContext_t structure + * flags - bit flags (unused - must be zero) + * + * Returns: + * + * The return value is zero if the stream is parsed successfully. + * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + */ + +int aclACLParse(NSErr_t * errp, ACLFile_t * acf, ACContext_t * acc, int flags) +{ + void * token = acf->acf_token; /* handle for current token */ + char * tokenstr; /* current token string */ + char * aclname; /* ACL name string */ + ACL_t * aclp; /* pointer to ACL structure */ + int rv; /* result value */ + int eid; /* error id value */ + char linestr[16]; /* line number string buffer */ + + /* Look for top-level statements */ + for (;;) { + + /* Get a token to begin a statement */ + rv = aclGetToken(errp, acf, 0); + + /* An identifier would be nice ... */ + if (rv != TOKEN_IDENT) { + + /* Empty statements are ok, if pointless */ + if (rv == TOKEN_EOS) continue; + + /* EOF is valid here */ + if (rv == TOKEN_EOF) break; + + /* Anything else is unacceptable */ + goto err_nostmt; + } + + /* Check identifier for statement keywords */ + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_ACL)) { + + /* ACL name rights-list { acl-def-list }; */ + + /* Get the name of the ACL */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_IDENT) goto err_aclname; + aclname = lex_token(token); + + /* Create the ACL structure */ + rv = aclCreate(errp, acc, aclname, &aclp); + if (rv < 0) goto punt; + + /* Get the next token after the ACL name */ + rv = aclGetToken(errp, acf, 0); + + /* Parse the rights specification */ + rv = aclRightsParse(errp, acf, acc, &aclp->acl_rights); + + /* Want a "{" to open the ACL directive list */ + if (rv != TOKEN_LBRACE) { + if (rv < 0) goto punt; + goto err_aclopen; + } + + /* Get the first token in the ACL directive list */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + + /* Parse the ACL directive list */ + rv = aclDirectivesParse(errp, acf, aclp); + + /* Want a "}" to close the ACL directive list */ + if (rv != TOKEN_RBRACE) { + if (rv < 0) goto punt; + goto err_aclclose; + } + } + else if (!strcasecmp(tokenstr, KEYWORD_INCLUDE)) { + /* Include "filename"; */ + } + else if (!strcasecmp(tokenstr, KEYWORD_REALM)) { + /* Realm name realm-spec */ + } + else if (!strcasecmp(tokenstr, KEYWORD_RIGHTS)) { + /* Rights name rights-def; */ + } + else if (!strcasecmp(tokenstr, KEYWORD_HOSTS)) { + /* Hosts name auth-hosts; */ + } + else goto err_syntax; + } + + return 0; + + err_nostmt: + eid = ACLERR1700; + rv = ACLERRPARSE; + goto err_ret; + + err_aclname: + eid = ACLERR1720; + rv = ACLERRPARSE; + goto err_ret; + + err_aclopen: + eid = ACLERR1740; + rv = ACLERRPARSE; + goto err_ret; + + err_aclclose: + eid = ACLERR1760; + rv = ACLERRPARSE; + goto err_ret; + + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; + + err_syntax: + eid = ACLERR1780; + rv = ACLERRPARSE; + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, tokenstr); + + punt: + return rv; +} + +/* + * Description (aclFileClose) + * + * This function closes an ACL file previously opened by aclFileOpen(), + * and frees any associated data structures. + * + * Arguments: + * + * acf - pointer to ACL file information + * flags - bit flags (unused - must be zero) + */ + +void aclFileClose(ACLFile_t * acf, int flags) +{ + if (acf != 0) { + + /* Destroy the associated lexer stream if any */ + if (acf->acf_lst != 0) { + lex_stream_destroy(acf->acf_lst); + } + + /* Close the file if it's open */ + if (acf->acf_fd != SYS_ERROR_FD) { + system_fclose(acf->acf_fd); + } + + /* Destroy any associated token */ + if (acf->acf_token != 0) { + lex_token_destroy(acf->acf_token); + } + + /* Free the filename string if any */ + if (acf->acf_filename != 0) { + FREE(acf->acf_filename); + } + + /* Free the ACLFile_t structure */ + FREE(acf); + } +} + +/* + * Description (aclFileOpen) + * + * This function opens a specified filename and creates a structure + * to contain information about the file during parsing. This + * includes a handle for a LEX data stream for the file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * filename - name of file to be opened + * flags - bit flags (unused - must be zero) + * pacf - pointer to returned ACLFile_t pointer + * + * Returns: + * + * The return value is zero if the file is opened successfully, and + * a pointer to the ACLFile_t is returned in the location specified + * by 'pacf'. Otherwise a negative error code (ACLERRxxxx - see + * aclerror.h) is returned, and an error frame will be generated if + * an error list is provided. + */ + +int aclFileOpen(NSErr_t * errp, + char * filename, int flags, ACLFile_t **pacf) +{ + ACLFile_t * acf; /* pointer to ACL file structure */ + int rv; /* return value */ + int eid; /* error identifier */ + char * errmsg; /* system error message string */ + + *pacf = 0; + + /* Allocate the ACLFile_t structure */ + acf = (ACLFile_t *)MALLOC(sizeof(ACLFile_t)); + if (acf == 0) goto err_nomem1; + + memset((void *)acf, 0, sizeof(ACLFile_t)); + acf->acf_filename = STRDUP(filename); + acf->acf_lineno = 1; + acf->acf_flags = flags; + + /* Create a LEX token object */ + rv = lex_token_new((pool_handle_t *)0, 32, 8, &acf->acf_token); + if (rv < 0) goto err_nomem2; + + /* Open the file */ + acf->acf_fd = system_fopenRO(acf->acf_filename); + if (acf->acf_fd == SYS_ERROR_FD) goto err_open; + + /* Create a LEX stream for the file */ + acf->acf_lst = lex_stream_create(aclStreamGet, + (void *)acf->acf_fd, 0, 8192); + if (acf->acf_lst == 0) goto err_nomem3; + + *pacf = acf; + return 0; + + err_open: /* file open error */ + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + goto punt; + + err_nomem1: /* MALLOC of ACLFile_t failed */ + rv = ACLERRNOMEM; + eid = ACLERR1920; + goto err_mem; + + err_nomem2: /* lex_token_new() failed */ + rv = ACLERRNOMEM; + eid = ACLERR1940; + goto err_mem; + + err_nomem3: /* lex_stream_create() failed */ + system_fclose(acf->acf_fd); + rv = ACLERRNOMEM; + eid = ACLERR1960; + + err_mem: + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; + + punt: + return rv; +} + +/* + * Description (aclGetDNSString) + * + * This function parses a DNS name specification, which consists + * of a sequence of DNS name components separated by ".". Each + * name component must start with a letter, and contains only + * letters, digits, and hyphens. An exception is that the first + * component may be the wildcard indicator, "*". This function + * assumes that the current token already contains a TOKEN_STAR + * or TOKEN_IDENT. The complete DNS name specification is + * returned as the current token string. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * + * Returns: + * + * The character terminating the DNS name specification is returned + * as the function value. The current token type is unchanged, but + * the string associated with the current token contains the + * complete DNS name specification. An error is indicated by a + * negative return value, and an error frame is generated if an + * error list is provided. + */ + +int aclGetDNSString(NSErr_t * errp, ACLFile_t * acf) +{ + LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */ + void * token = acf->acf_token; /* LEX token handle */ + int rv; /* result value */ + int eid; /* error id value */ + char linestr[16]; /* line number string buffer */ + + /* The current token should be TOKEN_STAR or TOKEN_IDENT */ + rv = acf->acf_ttype; + + if ((rv != TOKEN_STAR) && (rv != TOKEN_IDENT)) goto err_dns1; + + /* Loop to parse [ "." dns-component ]+ */ + for (;;) { + + /* Try to step over a "." */ + rv = lex_next_char(lst, aclChTab, 0); + + /* End of DNS string if there's not one there */ + if (rv != '.') break; + + /* Append the "." to the token string */ + (void)lex_token_append(token, 1, "."); + + /* Advance the input stream past the "." */ + rv = lex_next_char(lst, aclChTab, CCM_SPECIAL); + + /* Next we want to see a letter */ + rv = lex_next_char(lst, aclChTab, 0); + + /* Error if it's not there */ + if (!lex_class_check(aclChTab, rv, CCM_LETTER)) goto err_dns2; + + /* Append a string of letters, digits, hyphens to token */ + rv = lex_scan_over(lst, aclChTab, (CCM_LETTER|CCM_DIGIT|CCM_HYPHEN), + token); + if (rv < 0) goto err_dns3; + } + + punt: + return rv; + + err_dns1: + eid = ACLERR2100; + rv = ACLERRPARSE; + goto err_ret; + + err_dns2: + eid = ACLERR2120; + rv = ACLERRPARSE; + goto err_ret; + + err_dns3: + eid = ACLERR2140; + rv = ACLERRPARSE; + goto err_ret; + + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; +} + +int aclGetFileSpec(NSErr_t * errp, ACLFile_t * acf, int flags) +{ + LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */ + void * token = acf->acf_token; /* LEX token handle */ + char * tokenstr; /* token string pointer */ + int rv; /* result value */ + int eid; /* error id value */ + char linestr[16]; /* line number string buffer */ + + /* Skip whitespace */ + rv = lex_skip_over(lst, aclChTab, CCM_WS); + if (rv < 0) goto err_lex1; + + /* Begin a new token string */ + rv = lex_token_start(token); + + rv = lex_scan_over(lst, aclChTab, CCM_FILENAME, token); + if (rv < 0) goto err_lex2; + + tokenstr = lex_token(token); + + if (!tokenstr || !*tokenstr) goto err_nofn; + + punt: + return rv; + + err_lex1: + eid = ACLERR2900; + goto err_parse; + + err_lex2: + eid = ACLERR2920; + goto err_parse; + + err_nofn: + eid = ACLERR2940; + + err_parse: + rv = ACLERRPARSE; + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; +} + +/* + * Description (aclGetIPAddr) + * + * This function retrieves an IP address specification from a given + * input stream. The specification consists of an IP address expressed + * in the standard "." notation, possibly followed by whitespace and a + * netmask, also in "." form. The IP address and netmask values are + * returned. If no netmask is specified, a default value of 0xffffffff + * is returned. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * pip - pointer to returned IP address value + * pmask - pointer to returned IP netmask value + * + * Returns: + * + * If successful, the return value identifies the type of the token + * following the IP address specification. This token type value is + * also returned in acf_ttype. An error is indicated by a negative + * error code (ACLERRxxxx - see aclerror.h), and an error frame will + * be generated if an error list is provided. The token type code in + * acf_ttype is TOKEN_ERROR when an error code is returned. + */ + +int aclGetIPAddr(NSErr_t * errp, + ACLFile_t * acf, IPAddr_t * pip, IPAddr_t * pmask) +{ + LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */ + void * token = acf->acf_token; /* LEX token handle */ + char * tokenstr; /* token string pointer */ + IPAddr_t ipaddr; /* IP address */ + IPAddr_t netmask; /* IP netmask */ + int dotcnt; /* count of '.' seen in address */ + int rv; /* result value */ + int eid; /* error id value */ + char linestr[16]; /* line number string buffer */ + + /* Set default return values */ + *pip = 0; + *pmask = 0xffffffff; + + rv = acf->acf_ttype; + + /* The current token must be a number */ + if (rv != TOKEN_NUMBER) { + + /* No IP address present */ + return rv; + } + + /* Assume no netmask */ + netmask = 0xffffffff; + + for (dotcnt = 0;;) { + + /* Append digits and letters to the current token */ + rv = lex_scan_over(lst, aclChTab, (CCM_DIGIT|CCM_LETTER), token); + if (rv < 0) goto err_lex1; + + /* Stop when no "." follows the digits and letters */ + if (rv != '.') break; + + /* Stop if we've already seen three "." */ + if (++dotcnt > 3) break; + + /* Advance past the "." */ + (void)lex_next_char(lst, aclChTab, CCM_SPECIAL); + + /* Check the next character for a "*" */ + rv = lex_next_char(lst, aclChTab, 0); + if (rv == '*') { + + /* Advance past the "*" */ + (void)lex_next_char(lst, aclChTab, CCM_SPECIAL); + + netmask <<= ((4-dotcnt)*8); + netmask = htonl(netmask); + + while (dotcnt < 4) { + (void)lex_token_append(token, 2, ".0"); + ++dotcnt; + } + break; + } + else { + /* Append the "." to the token string */ + (void)lex_token_append(token, 1, "."); + } + } + + /* Get a pointer to the token string */ + tokenstr = lex_token(token); + + /* A NULL pointer or an empty string is an error */ + if (!tokenstr || !*tokenstr) goto err_noip; + + /* Convert IP address to binary */ + ipaddr = inet_addr(tokenstr); + if (ipaddr == (unsigned long)-1) goto err_badip; + + /* Skip whitespace */ + rv = lex_skip_over(lst, aclChTab, CCM_WS); + if (rv < 0) goto err_lex2; + + /* A digit is the start of a netmask */ + if ((netmask == 0xffffffff) && lex_class_check(aclChTab, rv, CCM_DIGIT)) { + + /* Initialize token for network mask */ + rv = lex_token_start(token); + + for (dotcnt = 0;;) { + + /* Collect token including digits, letters, and periods */ + rv = lex_scan_over(lst, aclChTab, (CCM_DIGIT|CCM_LETTER), token); + if (rv < 0) goto err_lex3; + + /* Stop when no "." follows the digits and letters */ + if (rv != '.') break; + + /* Stop if we've already seen three "." */ + if (++dotcnt > 3) break; + + /* Append the "." to the token string */ + (void)lex_token_append(token, 1, "."); + + /* Advance past the "." */ + (void)lex_next_char(lst, aclChTab, CCM_SPECIAL); + } + + /* Get a pointer to the token string */ + tokenstr = lex_token(token); + + /* A NULL pointer or an empty string is an error */ + if (!tokenstr || !*tokenstr) goto err_nonm; + + /* Convert netmask to binary. */ + netmask = inet_addr(tokenstr); + if (netmask == (unsigned long)-1) { + + /* + * Unfortunately inet_addr() doesn't distinguish between an + * error and a valid conversion of "255.255.255.255". So + * we check for it explicitly. Too bad if "0xff.0xff.0xff.0xff" + * is specified. Don't do that! + */ + if (strcmp(tokenstr, "255.255.255.255")) goto err_badnm; + } + } + + /* Return the IP address and netmask in host byte order */ + *pip = ntohl(ipaddr); + *pmask = ntohl(netmask); + + /* Get the token following the IP address (and netmask) */ + rv = aclGetToken(errp, acf, 0); + + punt: + acf->acf_ttype = (rv < 0) ? TOKEN_ERROR : rv; + return rv; + + err_lex1: + eid = ACLERR2200; + rv = ACLERRPARSE; + goto err_ret; + + err_lex2: + eid = ACLERR2220; + rv = ACLERRPARSE; + goto err_ret; + + err_lex3: + eid = ACLERR2240; + rv = ACLERRPARSE; + goto err_ret; + + err_noip: + eid = ACLERR2260; + rv = ACLERRPARSE; + goto err_ret; + + err_badip: + eid = ACLERR2280; + rv = ACLERRPARSE; + goto err_ret; + + err_nonm: + eid = ACLERR2300; + rv = ACLERRPARSE; + goto err_ret; + + err_badnm: + eid = ACLERR2320; + rv = ACLERRPARSE; + goto err_ret; + + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; +} + +/* + * Description (aclGetToken) + * + * This function retrieves the next token in an ACL definition file. + * It skips blank lines, comments, and white space. It updates + * the current line number as newlines are encountered. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * flags - bit flags: + * AGT_NOSKIP - don't skip leading whitespace + * AGT_APPEND - append to token buffer + * (else start new token) + * + * Returns: + * + * The return value is a code identifying the next token if successful. + * This token type value is also returned in acf_ttype. An error + * is indicated by a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + * The token type code in acf_ttype is TOKEN_ERROR when an error code + * is returned. + */ + +int aclGetToken(NSErr_t * errp, ACLFile_t * acf, int flags) +{ + LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */ + void * token = acf->acf_token; /* LEX token handle */ + int dospecial = 0; /* handle CCM_SPECIAL character */ + int tv; /* token value */ + int rv; /* result value */ + int eid; /* error id */ + char spech; + char linestr[16]; /* line number string buffer */ + + /* Begin a new token, unless AGT_APPEND is set */ + if (!(flags & AGT_APPEND)) { + rv = lex_token_start(token); + } + + /* Loop to read file */ + tv = 0; + do { + + /* + * If the AGT_NOSKIP flag is not set, skip whitespace (but not + * newline). If the flag is set, just get the next character. + */ + rv = lex_skip_over(lst, aclChTab, (flags & AGT_NOSKIP) ? 0 : CCM_WS); + if (rv <= 0) { + if (rv < 0) goto err_lex1; + + /* Exit loop if EOF */ + if (rv == 0) { + tv = TOKEN_EOF; + break; + } + } + + /* Analyze character after whitespace */ + switch (rv) { + + case '\n': /* newline */ + + /* Keep count of lines as we're skipping whitespace */ + acf->acf_lineno += 1; + (void)lex_next_char(lst, aclChTab, CCM_NL); + break; + + case '#': /* Beginning of comment */ + + /* Skip to a newline if so */ + rv = lex_skip_to(lst, aclChTab, CCM_NL); + break; + + case ';': /* End of statement */ + tv = TOKEN_EOS; + dospecial = 1; + break; + + case '@': /* at sign */ + tv = TOKEN_AT; + dospecial = 1; + break; + + case '+': /* plus sign */ + tv = TOKEN_PLUS; + dospecial = 1; + break; + + case '*': /* asterisk */ + tv = TOKEN_STAR; + dospecial = 1; + break; + + case '.': /* period */ + tv = TOKEN_PERIOD; + dospecial = 1; + break; + + case ',': /* comma */ + tv = TOKEN_COMMA; + dospecial = 1; + break; + + case '(': /* left parenthesis */ + tv = TOKEN_LPAREN; + dospecial = 1; + break; + + case ')': /* right parenthesis */ + tv = TOKEN_RPAREN; + dospecial = 1; + break; + + case '{': /* left brace */ + tv = TOKEN_LBRACE; + dospecial = 1; + break; + + case '}': /* right brace */ + tv = TOKEN_RBRACE; + dospecial = 1; + break; + + case '\"': /* double quote */ + case '\'': /* single quote */ + + /* Append string contents to token buffer */ + rv = lex_scan_string(lst, token, 0); + tv = TOKEN_STRING; + break; + + default: + + /* Check for identifier, beginning with a letter */ + if (lex_class_check(aclChTab, rv, CCM_LETTER)) { + + /* Append valid identifier characters to token buffer */ + rv = lex_scan_over(lst, aclChTab, CCM_IDENT, token); + tv = TOKEN_IDENT; + break; + } + + /* Check for a number, beginning with a digit */ + if (lex_class_check(aclChTab, rv, CCM_DIGIT)) { + char digit; + + /* Save the first digit */ + digit = (char)rv; + + /* Append the first digit to the token */ + rv = lex_token_append(token, 1, &digit); + + /* Skip over the first digit */ + rv = lex_next_char(lst, aclChTab, CCM_DIGIT); + + /* If it's '0', we might have "0x.." */ + if (rv == '0') { + + /* Pick up the next character */ + rv = lex_next_char(lst, aclChTab, 0); + + /* Is it 'x'? */ + if (rv == 'x') { + + /* Yes, append it to the token */ + digit = (char)rv; + rv = lex_token_append(token, 1, &digit); + + /* Step over it */ + rv = lex_next_char(lst, aclChTab, CCM_LETTER); + } + } + /* Get more digits, if any */ + rv = lex_scan_over(lst, aclChTab, CCM_DIGIT, token); + tv = TOKEN_NUMBER; + break; + } + + /* Unrecognized character */ + + spech = *lst->lst_cp; + lex_token_append(token, 1, &spech); + lst->lst_cp += 1; + lst->lst_len -= 1; + tv = TOKEN_HUH; + break; + } + + /* Handle CCM_SPECIAL character? */ + if (dospecial) { + + /* Yes, clear the flag for next time */ + dospecial = 0; + + /* Get the character and advance past it */ + rv = lex_next_char(lst, aclChTab, CCM_SPECIAL); + + /* Append the character to the token buffer */ + spech = (char)rv; + (void)lex_token_append(token, 1, &spech); + } + } + while ((tv == 0) && (rv > 0)); + + if (rv < 0) { + tv = TOKEN_ERROR; + } + else rv = tv; + + acf->acf_ttype = tv; + return rv; + + err_lex1: + rv = ACLERRPARSE; + eid = ACLERR2400; + + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + + acf->acf_ttype = TOKEN_ERROR; + return rv; +} + +/* + * Description (aclParseInit) + * + * This function is called to initialize the ACL parser. It + * creates a LEX character class table to assist in parsing. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, the return value is zero. An error is indicated + * by a negative return value. + */ + +int aclParseInit() +{ + int rv; /* result value */ + + /* Have we created the character class table yet? */ + if (aclChTab == 0) { + + /* No, initialize character classes for lexer processing */ + rv = lex_class_create(classc, classv, &aclChTab); + if (rv < 0) goto err_nomem; + } + + return 0; + + err_nomem: + return ACLERRNOMEM; +} + +/* + * Description (aclRealmSpecParse) + * + * This function parses an authentication realm specification. An + * authentication realm includes an authentication database and + * an authentication method. The syntax of a realm-spec is: + * + * realm-spec ::= "{" realm-directive-list "}" | "realm" realm-name + * realm-directive-list ::= realm-directive | + * realm-directive-list ";" realm-directive + * realm-directive ::= realm-db-directive | realm-meth-directive + * | realm-prompt-directive + * realm-db-directive ::= "database" db-file-path + * realm-meth-directive ::= "method" auth-method-name + * auth-method-name ::= "basic" | "SSL" + * realm-prompt-directive ::= "prompt" quote-char string quote-char + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acc - pointer to ACContext_t structure + * rspp - pointer to RealmSpec_t pointer + * + * Returns: + * + * If successful, the return value is the token type of the token + * following the realm-spec, i.e. either the first token after a + * realm-name or the first token after the closing "}". It is the + * caller's responsibility to validate this token as a legitimate + * successor of a realm-spec. If a parsing error occurs in the + * middle of a realm-spec, the return value is ACLERRPARSE, and an + * error frame is generated if an error list is provided. For + * other kinds of errors a negative error code (from aclerror.h) + * is returned. + */ + +int aclRealmSpecParse(NSErr_t * errp, + ACLFile_t * acf, ACContext_t * acc, RealmSpec_t **rspp) +{ + void * token = acf->acf_token; /* handle for current token */ + char * tokenstr; /* current token string */ + RealmSpec_t * rsp; /* realm spec pointer */ + RealmSpec_t * nrsp; /* named realm spec pointer */ + int rv; /* result value */ + int eid; /* error id value */ + char linestr[16]; /* line number string buffer */ + + rv = acf->acf_ttype; + + /* Is the current token a "{" ? */ + if (rv != TOKEN_LBRACE) { + + /* No, could it be KEYWORD_REALM? */ + if (rv == TOKEN_IDENT) { + + tokenstr = lex_token(token); + + if (!strcasecmp(tokenstr, KEYWORD_REALM)) { + + /* Yes, step to the realm name */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_IDENT) { + if (rv < 0) goto punt; + goto err_rlmname; + } + + tokenstr = lex_token(token); + + /* Look up the named realm specification */ + rv = symTableFindSym(acc->acc_stp, tokenstr, ACLSYMREALM, + (void **)&nrsp); + if (rv < 0) goto err_undrlm; + + /* Return the named realm specification */ + *rspp = nrsp; + + /* Step to the token after the realm name */ + rv = aclGetToken(errp, acf, 0); + } + } + + return rv; + } + + /* Step to the token after the "{" */ + rv = aclGetToken(errp, acf, 0); + if (rv < 0) goto punt; + + rsp = *rspp; + if (rsp == 0) { + rsp = (RealmSpec_t *)MALLOC(sizeof(RealmSpec_t)); + if (rsp == 0) goto err_nomem; + memset((void *)rsp, 0, sizeof(RealmSpec_t)); + rsp->rs_sym.sym_type = ACLSYMREALM; + *rspp = rsp; + } + + /* Loop for each realm-directive */ + for (;; rv = aclGetToken(errp, acf, 0)) { + + if (rv != TOKEN_IDENT) { + + /* Exit loop on "}" */ + if (rv == TOKEN_RBRACE) break; + + /* Ignore null directives */ + if (rv == TOKEN_EOS) continue; + + /* Otherwise need an identifier to start a directive */ + goto err_nodir; + } + + tokenstr = lex_token(token); + + /* Figure out which realm-directive this is */ + if (!strcasecmp(tokenstr, KEYWORD_DATABASE)) { + + /* Get a file specification for the database */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_STRING) { + if (rv < 0) goto punt; + goto err_nodb; + } + + rsp->rs_realm.rlm_dbname = lex_token_take(token); + rsp->rs_realm.rlm_aif = &NSADB_AuthIF; + } + else if (!strcasecmp(tokenstr, KEYWORD_METHOD)) { + + /* Step to the method identifier */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_IDENT) { + if (rv < 0) goto punt; + goto err_nometh; + } + + tokenstr = lex_token(token); + + /* Interpret method name and set method in realm structure */ + if (!strcasecmp(tokenstr, KEYWORD_BASIC)) { + rsp->rs_realm.rlm_ameth = AUTH_METHOD_BASIC; + } + else if (!strcasecmp(tokenstr, KEYWORD_SSL) && server_enterprise) { + rsp->rs_realm.rlm_ameth = AUTH_METHOD_SSL; + } + else goto err_badmeth; + } + else if (!strcasecmp(tokenstr, KEYWORD_PROMPT)) { + + /* Step to the realm prompt string */ + rv = aclGetToken(errp, acf, 0); + if ((rv != TOKEN_STRING) && (rv != TOKEN_IDENT)) { + if (rv < 0) goto punt; + goto err_noprompt; + } + + /* Reference a copy of the prompt string from the realm */ + rsp->rs_realm.rlm_prompt = lex_token_take(token); + } + else goto err_baddir; + + /* Get the token after the realm-directive */ + rv = aclGetToken(errp, acf, 0); + + /* Need a ";" to keep going */ + if (rv != TOKEN_EOS) break; + } + + if (rv != TOKEN_RBRACE) goto err_rbrace; + + /* Get the token after the realm-spec */ + rv = aclGetToken(errp, acf, 0); + + punt: + return rv; + + err_rlmname: + eid = ACLERR2500; + goto err_parse; + + err_undrlm: + eid = ACLERR2520; + rv = ACLERRUNDEF; + goto err_sym; + + err_nomem: + eid = ACLERR2540; + rv = ACLERRNOMEM; + goto ret_err; + + err_nodir: + eid = ACLERR2560; + goto err_parse; + + err_nodb: + eid = ACLERR2570; + goto err_parse; + + err_nometh: + eid = ACLERR2580; + goto err_parse; + + err_badmeth: + eid = ACLERR2600; + goto err_sym; + + err_noprompt: + eid = ACLERR2605; + goto err_parse; + + err_baddir: + eid = ACLERR2610; + goto err_sym; + + err_rbrace: + eid = ACLERR2620; + goto err_parse; + + err_sym: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, tokenstr); + goto punt; + + err_parse: + rv = ACLERRPARSE; + ret_err: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + goto punt; +} + +/* + * Description (aclRightsParse) + * + * This function parse an access rights list. The syntax for an + * access rights list is: + * + * rights-list ::= "(" list-of-rights ")" + * list-of-rights ::= rights-elem | list-of-rights "," rights-elem + * rights-elem ::= right-name | "+" rights-def-name + * right-name ::= identifier + * rights-def-name ::= identifier + * + * An element of a rights list is either the name of a particular + * access right (e.g. Read), or the name associated with an + * external definition of an access rights list, preceded by "+" + * (e.g. +editor-rights). The list is enclosed in parentheses, + * and the elements are separated by commas. + * + * This function adds to a list of rights provided by the caller. + * Access rights are internally assigned unique integer identifiers, + * and a symbol table is maintained to map an access right name to + * its identifier. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acf - pointer to ACLFile_t for ACL file + * acc - pointer to ACContext_t structure + * rights - pointer to rights list head + * + * Returns: + * + * The return value is a code identifying the next token if successful. + * End-of-stream is indicated by a return value of TOKEN_EOF. An error + * is indicated by a negative error code (ACLERRxxxx - see aclerror.h), + * and an error frame will be generated if an error list is provided. + */ + +int aclRightsParse(NSErr_t * errp, ACLFile_t * acf, ACContext_t * acc, + RightSpec_t **rights) +{ + void * token = acf->acf_token; /* LEX token handle */ + char * ename; /* element name string pointer */ + RightSpec_t * rsp; /* rights specification pointer */ + RightSpec_t * nrsp; /* named rights spec pointer */ + RightDef_t * rdp; /* right definition pointer */ + int rv; /* result value */ + int eid; /* error id */ + char linestr[16]; /* line number string buffer */ + + /* Look for a left parenthesis */ + if (acf->acf_ttype != TOKEN_LPAREN) { + + /* No rights list present */ + return 0; + } + + rsp = *rights; + + /* Create a RightSpec_t if we don't have one */ + if (rsp == 0) { + rsp = (RightSpec_t *)MALLOC(sizeof(RightSpec_t)); + if (rsp == 0) goto err_nomem1; + memset((void *)rsp, 0, sizeof(RightSpec_t)); + rsp->rs_sym.sym_type = ACLSYMRDEF; + *rights = rsp; + } + + /* Parse list elements */ + for (;;) { + + /* Look for an identifier */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_IDENT) { + + /* No, maybe a "+" preceding a rights definition name? */ + if (rv != TOKEN_PLUS) { + + /* One more chance, we'll allow the closing ")" after "," */ + if (rv != TOKEN_RPAREN) { + /* No, bad news */ + if (rv < 0) goto punt; + goto err_elem; + } + + /* Got right paren after comma */ + break; + } + + /* Got a "+", try for the rights definition name */ + rv = aclGetToken(errp, acf, 0); + if (rv != TOKEN_IDENT) { + if (rv < 0) goto punt; + goto err_rdef; + } + + /* Get a pointer to the token string */ + ename = lex_token(token); + + /* See if it matches a rights definition in the symbol table */ + rv = symTableFindSym(acc->acc_stp, ename, ACLSYMRDEF, + (void **)&nrsp); + if (rv) goto err_undef; + + /* + * Merge the rights from the named rights list into the + * current rights list. + */ + rv = uilMerge(&rsp->rs_list, &nrsp->rs_list); + if (rv < 0) goto err_nomem2; + } + else { + + /* The current token is an access right name */ + + /* Get a pointer to the token string */ + ename = lex_token(token); + + + /* Find or create an access right definition */ + rv = aclRightDef(errp, acc, ename, &rdp); + if (rv < 0) goto err_radd; + + /* Add the id for this right to the current rights list */ + rv = usiInsert(&rsp->rs_list, rdp->rd_id); + if (rv < 0) goto err_nomem3; + } + + rv = aclGetToken(errp, acf, 0); + + /* Want a comma to continue the list */ + if (rv != TOKEN_COMMA) { + + /* A right parenthesis will end the list nicely */ + if (rv == TOKEN_RPAREN) { + + /* Get the first token after the rights list */ + rv = aclGetToken(errp, acf, 0); + break; + } + + /* Anything else is an error */ + if (rv < 0) goto punt; + goto err_list; + } + } + + return rv; + + err_elem: + eid = ACLERR2700; + rv = ACLERRSYNTAX; + goto err_ret; + + err_rdef: + eid = ACLERR2720; + rv = ACLERRSYNTAX; + goto err_ret; + + err_undef: + eid = ACLERR2740; + rv = ACLERRUNDEF; + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, + 3, acf->acf_filename, linestr, ename); + return rv; + + err_nomem1: + eid = ACLERR2760; + goto err_nomem; + + err_nomem2: + eid = ACLERR2780; + goto err_nomem; + + err_radd: + eid = ACLERR2800; + goto err_ret; + + err_nomem3: + eid = ACLERR2820; + goto err_nomem; + + err_nomem: + rv = ACLERRNOMEM; + goto err_ret; + + err_list: + + eid = ACLERR2840; + rv = ACLERRSYNTAX; + + err_ret: + sprintf(linestr, "%d", acf->acf_lineno); + nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr); + + punt: + return rv; +} + +/* + * Description (aclStreamGet) + * + * This function is the stream read function designated by + * aclFileOpen() to read the file associated with the LEX stream + * it creates. It reads the next chunk of the file into the + * stream buffer. + * + * Arguments: + * + * lst - pointer to LEX stream structure + * + * Returns: + * + * The return value is the number of bytes read if successful. + * A return value of zero indicates end-of-file. An error is + * indicated by a negative return value. + */ + +int aclStreamGet(LEXStream_t * lst) +{ + SYS_FILE fd = (SYS_FILE)(lst->lst_strmid); + int len; + + len = system_fread(fd, lst->lst_buf, lst->lst_buflen); + if (len >= 0) { + lst->lst_len = len; + lst->lst_cp = lst->lst_buf; + } + + return len; +} diff --git a/lib/libaccess/aclpriv.h b/lib/libaccess/aclpriv.h new file mode 100644 index 00000000..a93cc055 --- /dev/null +++ b/lib/libaccess/aclpriv.h @@ -0,0 +1,171 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * ACL private data structure definitions + */ + +#ifndef ACL_PARSER_HEADER +#define ACL_PARSER_HEADER + +#include <netsite.h> +#include <plhash.h> +#include <base/pool.h> +#include <base/plist.h> +#include <libaccess/las.h> + + +#define ACL_TERM_BSIZE 4 +#define ACL_FALSE_IDX -2 +#define ACL_TRUE_IDX -1 +#define ACL_MIN_IDX 0 +#define ACL_EXPR_STACK 1024 +#define ACL_TABLE_THRESHOLD 10 + +typedef enum { + ACL_EXPR_OP_AND, + ACL_EXPR_OP_OR, + ACL_EXPR_OP_NOT + } ACLExprOp_t; + + +typedef struct ACLExprEntry { + char *attr_name; /* LAS name input */ + CmpOp_t comparator; /* LAS comparator input */ + char *attr_pattern; /* LAS attribute input */ + int false_idx; /* index, -1 true, -2 false */ + int true_idx; /* index, -1 true, -2 false */ + int start_flag; /* marks start of an expr */ + void *las_cookie; /* private data store for LAS */ + LASEvalFunc_t las_eval_func; /* LAS function */ +} ACLExprEntry_t; + +typedef struct ACLExprRaw { + char *attr_name; /* expr lval */ + CmpOp_t comparator; /* comparator */ + char *attr_pattern; /* expr rval */ + ACLExprOp_t logical; /* logical operator */ +} ACLExprRaw_t; + +typedef struct ACLExprStack { + char *expr_text[ACL_EXPR_STACK]; + ACLExprRaw_t *expr[ACL_EXPR_STACK]; + int stack_index; + int found_subexpression; + int last_subexpression; +} ACLExprStack_t; + +typedef struct ACLExprHandle { + char *expr_tag; + char *acl_tag; + int expr_number; + ACLExprType_t expr_type; + int expr_flags; + int expr_argc; + char **expr_argv; + PList_t expr_auth; + ACLExprEntry_t *expr_arry; + int expr_arry_size; + int expr_term_index; + ACLExprRaw_t *expr_raw; + int expr_raw_index; + int expr_raw_size; + struct ACLExprHandle *expr_next; /* Null-terminated */ +} ACLExprHandle_t; + +typedef struct ACLHandle { + int ref_count; + char *tag; + PFlags_t flags; + char *las_name; + pblock *pb; + char **attr_name; + int expr_count; + ACLExprHandle_t *expr_list_head; /* Null-terminated */ + ACLExprHandle_t *expr_list_tail; +} ACLHandle_t; + + +typedef struct ACLWrapper { + ACLHandle_t *acl; + struct ACLWrapper *wrap_next; +} ACLWrapper_t; + +#define ACL_LIST_STALE 0x1 +#define ACL_LIST_IS_STALE(x) ((x)->flags & ACL_LIST_STALE) + +typedef struct ACLListHandle { + ACLWrapper_t *acl_list_head; /* Null-terminated */ + ACLWrapper_t *acl_list_tail; /* Null-terminated */ + int acl_count; + void *acl_sym_table; + void *cache; + uint32 flags; + int ref_count; +} ACLListHandle_t; + +typedef struct ACLAceNumEntry { + int acenum; + struct ACLAceNumEntry *next; + struct ACLAceNumEntry *chain; /* only used for freeing memory */ +} ACLAceNumEntry_t; + +typedef struct ACLAceEntry { + ACLExprHandle_t *acep; + /* Array of auth block ptrs for all the expr + clauses in this ACE */ + PList_t *autharray; + /* PList with auth blocks for ALL attributes */ + PList_t global_auth; + struct ACLAceEntry *next; /* Null-terminated list */ +} ACLAceEntry_t; + +typedef struct PropList PropList_t; + +typedef struct ACLEvalHandle { + pool_handle_t *pool; + ACLListHandle_t *acllist; + PList_t subject; + PList_t resource; + int default_result; +} ACLEvalHandle_t; + + +typedef struct ACLListCache { +/* Hash table for all access rights used in all acls in this list. Each + * hash entry has a list of ACE numbers that relate to this referenced + * access right. + */ + PLHashTable *Table; + char *deny_response; + char *deny_type; + ACLAceEntry_t *acelist; /* Evaluation order + * list of all ACEs + */ + ACLAceNumEntry_t *chain_head; /* Chain of all Ace num + * entries for this + * ACL list so we can free them + */ + ACLAceNumEntry_t *chain_tail; +} ACLListCache_t; + +/* this is to speed up acl_to_str_append */ +typedef struct acl_string_s { + char * str; + long str_size; + long str_len; +} acl_string_t; + + + +NSPR_BEGIN_EXTERN_C +extern int ACL_ExprDisplay( ACLExprHandle_t *acl_expr ); +extern int ACL_AssertAcl( ACLHandle_t *acl ); +extern int ACL_EvalDestroyContext ( ACLListCache_t *cache ); +extern time_t *acl_get_req_time(PList_t resource); +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/aclscan.h b/lib/libaccess/aclscan.h new file mode 100644 index 00000000..df54f374 --- /dev/null +++ b/lib/libaccess/aclscan.h @@ -0,0 +1,25 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Internal functions for scanner. + */ + + +#ifndef ACLSCAN_H +#define ACLSCAN_H + +NSPR_BEGIN_EXTERN_C + +extern int acl_InitScanner(NSErr_t *errp, char *filename, char *buffer); +extern int acl_EndScanner(void); +extern void aclerror(const char *s); +extern int acllex(void); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/aclscan.l b/lib/libaccess/aclscan.l new file mode 100644 index 00000000..009e02db --- /dev/null +++ b/lib/libaccess/aclscan.l @@ -0,0 +1,379 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Lexical analyzer for ACL + */ + +%{ +#include "acl.tab.h" /* token codes */ +#include <base/file.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "aclpriv.h" +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <libaccess/aclerror.h> +#ifdef XP_WIN32 +#include <io.h> +#endif + +#include "parse.h" +#include "aclscan.h" + +static int acl_scanner_input(char *buffer, int max_size); + +#define YY_NEVER_INTERACTIVE 1 +#undef YY_INPUT +#define YY_INPUT(buf, result, max) (result = acl_scanner_input(buf, max)) + +static int acl_lineno; +static int acl_tokenpos; +static char acl_filename[500]; +static NSErr_t *acl_errp; +static int acl_use_buffer; +static char *acl_buffer; +static int acl_buffer_length; +static int acl_buffer_offset; +static char *last_string; +static SYS_FILE acl_prfd; + + +%} + +ws [ \t]+ +comment #.* +qstring \"[^\"\n]*[\"\n] +variable [\*a-zA-Z0-9\.\-\_][\*a-zA-Z0-9\.\-\_]* + +%% + +\n.* { + acl_lineno++; + acl_tokenpos = 0; + yyless(1); + } + +{ws} ; + +{comment} ; + +<<EOF>> { + yylval.string = NULL; + last_string = yylval.string; + return(0); + } + +{qstring} { + yylval.string = PERM_STRDUP( yytext+1 ); + last_string = yylval.string; + if ( yylval.string[yyleng-2] != '"' ) + fprintf(stderr, "Unterminated string\n") ; + else + yylval.string[yyleng-2] = '\0'; + acl_tokenpos += yyleng; + return ACL_QSTRING_TOK; + } + + +absolute { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ABSOLUTE_TOK; + } + +acl { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ACL_TOK; + } + +allow { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ALLOW_TOK; + } + +always { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ALWAYS_TOK; + } + +at { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_AT_TOK; + } + +authenticate { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_AUTHENTICATE_TOK; + } + +content { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_CONTENT_TOK; + } + +default { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_DEFAULT_TOK; + } + +deny { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_DENY_TOK; + } + +in { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_IN_TOK; + } + +inherit { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_INHERIT_TOK; + } + +terminal { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_TERMINAL_TOK; + } + +version { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_VERSION_TOK; + } + +with { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_WITH_TOK; + } + +not { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_NOT_TOK; + } + +and { + last_string = NULL; + yylval.ival = ACL_EXPR_OP_AND; + acl_tokenpos += yyleng; + return ACL_AND_TOK; + } + +or { + last_string = NULL; + yylval.ival = ACL_EXPR_OP_OR; + acl_tokenpos += yyleng; + return ACL_OR_TOK; + } + +"=" { + last_string = NULL; + yylval.ival = CMP_OP_EQ; + acl_tokenpos += yyleng; + return ACL_EQ_TOK; + } + +">=" { + last_string = NULL; + yylval.ival = CMP_OP_GE; + acl_tokenpos += yyleng; + return ACL_GE_TOK; + } + +">" { + last_string = NULL; + yylval.ival = CMP_OP_GT; + acl_tokenpos += yyleng; + return ACL_GT_TOK; + } + +"<" { + last_string = NULL; + yylval.ival = CMP_OP_LT; + acl_tokenpos += yyleng; + return ACL_LT_TOK; + } + +"<=" { + last_string = NULL; + yylval.ival = CMP_OP_LE; + acl_tokenpos += yyleng; + return ACL_LE_TOK; + } + +"!=" { + last_string = NULL; + yylval.ival = CMP_OP_NE; + acl_tokenpos += yyleng; + return ACL_NE_TOK; + } + +[(){},;] { + last_string = NULL; + acl_tokenpos += yyleng; + return yytext[0]; + } + +{variable} { + acl_tokenpos += yyleng; + yylval.string = PERM_STRDUP( yytext ); + last_string = yylval.string; + return ACL_VARIABLE_TOK; + } +%% + +void +acl_detab(char *t, char *s) +{ + int ii; + int pos; + int len; + + if (s == NULL || t == NULL) + return; + + len = strlen(s); + pos = 0; + for (ii = 0; ii < len; ii++) { + if (s[ii] == '\t') { + t[pos] = ' '; + } else { + t[pos] = s[ii]; + } + pos++; + } + t[pos] = '\0'; + return; +} + +void +yyerror(const char *s) +{ +char errorStr[256]; + +#if defined(UTEST) || defined(ACL_COMPILER) + printf("ACL file: %s\n", acl_filename); + printf("Syntax error at line: %d, token: %s\n", acl_lineno, yytext); + if ( last_string ) + free(last_string); +#else + sprintf(errorStr, "%d", acl_lineno); + if (yytext) { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 3, acl_filename, errorStr, yytext); + } else { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 2, acl_filename, errorStr); + } + if ( last_string ) + free(last_string); +#endif + +} + +int +acl_InitScanner(NSErr_t *errp, char *filename, char *buffer) +{ + acl_errp = errp; + acl_lineno = 1; + acl_use_buffer = (filename == NULL) ? 1 : 0 ; + if ( filename != NULL ) { + strcpy(acl_filename, filename); +#ifdef UTEST + yyin = fopen(filename, "r"); + if ( yyin == NULL ) { + return(-1); + } +#else + acl_prfd = system_fopenRO(filename); + if ( acl_prfd == NULL ) { + return(-1); + } + yyin = (FILE *) acl_prfd; +#endif + yyrestart(yyin); + } else if ( buffer != NULL ) { + strcpy(acl_filename, "internal-buffer"); + acl_buffer_offset = 0; + acl_buffer_length = strlen(buffer); + acl_buffer = PERM_STRDUP(buffer); + if (acl_buffer == NULL) + return(-1); + yyrestart(NULL); + } else { + return(-1); + } + return(0); +} + +int +acl_EndScanner() +{ + acl_lineno = 0; + if ( acl_use_buffer) { + if ( acl_buffer ) + PERM_FREE(acl_buffer); + } else if ( yyin ) { +#ifdef UTEST + fclose(yyin); +#else + if ( acl_prfd ) { + system_fclose(acl_prfd); + acl_prfd = NULL; + } +#endif + yyin = NULL ; + } + return(0); +} + +int +yywrap() +{ + return(1); +} + +static int +acl_scanner_input(char *buffer, int max_size) +{ + int len = 0; + + if ( acl_use_buffer ) { + len = (acl_buffer_length > max_size) ? max_size : + acl_buffer_length; + memcpy(buffer, (const void *) &acl_buffer[acl_buffer_offset], + len); + acl_buffer_length -= len; + acl_buffer_offset += len; + } +#ifdef UTEST + else if ( ((len = fread( buffer, 1, max_size, aclin )) == 0) + && ferror( aclin ) ) { + yyerror( "scanner input failed" ); + } +#else + else if ( (len = system_fread( acl_prfd, buffer, max_size)) < 0 ) { + yyerror( "scanner input failed" ); + } +#endif + + return(len); +} diff --git a/lib/libaccess/aclspace.cpp b/lib/libaccess/aclspace.cpp new file mode 100644 index 00000000..fb211075 --- /dev/null +++ b/lib/libaccess/aclspace.cpp @@ -0,0 +1,37 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclglobal.h> + +/* Ordered list of generic rights */ +char *generic_rights[7] = { + "read", + "write", + "execute", + "delete", + "info", + "list", + NULL + } ; + +char *http_generic[7] = { + "http_get, http_head, http_trace, http_revlog, http_options, http_copy, http_getattribute, http_index, http_getproperties, http_getattributenames ", + "http_put, http_mkdir, http_startrev, http_stoprev, http_edit, http_post, http_save, http_setattribute, http_revadd, http_revlabel, http_lock, http_unlock, http_unedit, http_stoprev, http_startrev", + "http_post", + "http_delete, http_destroy, http_move", + "http_head, http_trace, http_options", + "http_index", + NULL + } ; + +/* Pointer to all global ACL data. This pointer is moved (atomically) + when a cache flush call is made. +*/ +ACLGlobal_p ACLGlobal; +ACLGlobal_p oldACLGlobal; diff --git a/lib/libaccess/acltext.y b/lib/libaccess/acltext.y new file mode 100644 index 00000000..01d6ae1f --- /dev/null +++ b/lib/libaccess/acltext.y @@ -0,0 +1,957 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * This grammar is intended to parse the version 3.0 + * and version 2.0 ACL text files and output an ACLListHandle_t + * structure. + */ + +%{ +#include <string.h> +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/nserror.h> +#include "parse.h" +#include "aclscan.h" + +#define MAX_LIST_SIZE 255 +static ACLListHandle_t *curr_acl_list; /* current acl list */ +static ACLHandle_t *curr_acl; /* current acl */ +static ACLExprHandle_t *curr_expr; /* current expression */ +static PFlags_t pflags; /* current authorization flags */ +static char *curr_args_list[MAX_LIST_SIZE]; /* current args */ +static char *curr_user_list[MAX_LIST_SIZE]; /* current users v2 */ +static char *curr_ip_dns_list[MAX_LIST_SIZE]; /* current ip/dns v2 */ +static PList_t curr_auth_info; /* current authorization method */ +static int use_generic_rights; /* use generic rights for conversion */ + +int acl_PushListHandle(ACLListHandle_t *handle) +{ + curr_acl_list = handle; + return(0); +} + +static void +acl_string_lower(char *s) +{ +int ii; +int len; + + len = strlen(s); + for (ii = 0; ii < len; ii++) + s[ii] = tolower(s[ii]); + + return; +} + +static void +acl_clear_args(char **args_list) +{ + args_list[0] = NULL; +} + +static void +acl_add_arg(char **args_list, char *arg) +{ + static int args_index; + + if ( args_list[0] == NULL ) { + args_index = 0; + } + args_list[args_index] = arg; + args_index++; + args_list[args_index] = NULL; +} + +static void +acl_free_args(char **args_list) +{ + int ii; + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) + free(args_list[ii]); + else + break; + } +} + +static int +acl_set_args(ACLExprHandle_t *expr, char **args_list) +{ + int ii; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) { + if ( ACL_ExprAddArg(NULL, expr, args_list[ii]) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + } else + break; + } + return(0); +} + +static int +acl_set_users(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_users_or_groups(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + if ( ACL_ExprTerm(NULL, expr, "group", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < (ii * 2) - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_ip_dns(ACLExprHandle_t *expr, char **ip_dns) +{ + int ii; + int jj; + int len; + char *attr; + char *val; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( ip_dns[ii] ) { + + attr = "ip"; + val = ip_dns[ii]; + len = strlen(val); + + for (jj = 0; jj < len; jj++) { + if ( strchr("0123456789.*", val[jj]) == 0 ) { + attr = "dns"; + break; + } + } + + if ( ACL_ExprTerm(NULL, expr, attr, CMP_OP_EQ, + val) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(ip_dns); + return(-1); + } + + } else + break; + } + + acl_free_args(ip_dns); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + + return(0); +} + + +%} + +%union { + char *string; + int ival; +} + +%token ACL_ABSOLUTE_TOK +%token ACL_ACL_TOK +%token ACL_ALLOW_TOK +%token ACL_ALWAYS_TOK +%token ACL_AND_TOK +%token ACL_AT_TOK +%token ACL_AUTHENTICATE_TOK +%token ACL_CONTENT_TOK +%token ACL_DEFAULT_TOK +%token ACL_DENY_TOK +%token ACL_GROUP_TOK +%token ACL_IN_TOK +%token ACL_INHERIT_TOK +%token ACL_NOT_TOK +%token ACL_NULL_TOK +%token ACL_OR_TOK +%token <string> ACL_QSTRING_TOK +%token ACL_READ_TOK +%token ACL_TERMINAL_TOK +%token <string> ACL_VARIABLE_TOK +%token ACL_VERSION_TOK +%token ACL_WRITE_TOK +%token ACL_WITH_TOK + +%token <ival> ACL_EQ_TOK +%token <ival> ACL_GE_TOK +%token <ival> ACL_GT_TOK +%token <ival> ACL_LE_TOK +%token <ival> ACL_LT_TOK +%token <ival> ACL_NE_TOK + +%% + +/* + * If no version is specified then we have a version 2.0 ACL. + */ +start: | start_acl_v2 + | ACL_VERSION_TOK ACL_VARIABLE_TOK + { + free($<string>2); + } + ';' start_acl_v3 + ; + +/* + ************************************************************ + * Parse version 2.0 ACL + ************************************************************ + */ +start_acl_v2: acl_list_v2 + ; + +acl_list_v2: acl_v2 + | acl_list_v2 acl_v2 + ; + +acl_v2: ACL_ACL_TOK acl_name_v2 + '(' arg_list_v2 ')' '{' directive_list_v2 '}' + { + acl_free_args(curr_args_list); + } + ; + +acl_name_v2: ACL_VARIABLE_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>1); + free($<string>1); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } + | ACL_QSTRING_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>1); + free($<string>1); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } + ; + +arg_list_v2: arg_v2 + | arg_v2 ',' arg_list_v2 + ; + +arg_v2: ACL_VARIABLE_TOK + { + char acl_tmp_arg[255]; + char *acl_new_arg; + + if (!use_generic_rights) { + acl_string_lower($<string>1); + strcpy(acl_tmp_arg, "http_"); + strcat(acl_tmp_arg, $<string>1); + PERM_FREE($<string>1); + acl_new_arg = PERM_STRDUP(acl_tmp_arg); + acl_add_arg(curr_args_list, acl_new_arg); + } else { + PERM_FREE($<string>1); + } + } + | ACL_QSTRING_TOK + { + if (!use_generic_rights) { + acl_add_arg(curr_args_list, $<string>1); + } else { + PERM_FREE($<string>1); + } + } + ; + +directive_list_v2: directive_v2 ';' + | directive_v2 ';' directive_list_v2 + ; + +directive_v2: auth_method_v2 + | auth_statement_v2 + ; + +auth_statement_v2: ACL_ALWAYS_TOK auth_type_v2 + { + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } + } + host_spec_list_action_v2 + | ACL_DEFAULT_TOK auth_type_v2 host_spec_list_action_v2 + ; + +auth_type_v2: ACL_ALLOW_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } + | ACL_DENY_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } + ; + +auth_method_v2: + ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(auth) failed"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + realm_definition_v2 + | ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(auth) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + realm_definition_v2 + ; + +host_spec_list_action_v2: user_expr_v2 ACL_AT_TOK host_spec_list_v2 + { + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + yyerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_ip_dns(curr_expr, curr_ip_dns_list) < 0 ) { + yyerror("acl_set_ip_dns() failed"); + return(-1); + } + + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprAnd() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + yyerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + | user_expr_v2 + { + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + yyerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + yyerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +user_expr_v2: user_v2 + | '(' user_list_v2 ')' + ; + +user_list_v2: user_v2 + | user_v2 ',' user_list_v2 + ; + +user_v2: ACL_VARIABLE_TOK + { + acl_add_arg(curr_user_list, $<string>1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_user_list, $<string>1); + } + ; + + +host_spec_list_v2: dns_spec_v2 + | ip_spec_v2 + | '(' dns_ip_spec_list_v2 ')' + ; + +dns_spec_v2: ACL_VARIABLE_TOK + { + acl_add_arg(curr_ip_dns_list, $<string>1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_ip_dns_list, $<string>1); + } + ; + +ip_spec_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK + { + char tmp_str[255]; + + util_sprintf(tmp_str, "%s+%s", $<string>1, $<string>2); + free($<string>1); + free($<string>2); + acl_add_arg(curr_ip_dns_list, PERM_STRDUP(tmp_str)); + } + ; + +dns_ip_spec_list_v2: dns_spec_v2 + | ip_spec_v2 + | dns_spec_v2 ',' dns_ip_spec_list_v2 + | ip_spec_v2 ',' dns_ip_spec_list_v2 + ; + +realm_definition_v2: '{' methods_list_v2 '}' + { + if ( ACL_ExprAddArg(NULL, curr_expr, "user") < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAddArg(NULL, curr_expr, "group") < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +method_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK ';' + { + acl_string_lower($<string>1); + if (strcmp($<string>1, "database") == 0) { + free($<string>1); + free($<string>2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) { + } + free($<string>1); + } + } + | ACL_VARIABLE_TOK ACL_QSTRING_TOK ';' + { + acl_string_lower($<string>1); + if (strcmp($<string>1, "database") == 0) { + free($<string>1); + free($<string>2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) { + } + free($<string>1); + } + } + ; + +methods_list_v2: method_v2 + | method_v2 methods_list_v2 + ; + +/* + ************************************************************ + * Parse version 3.0 ACL + ************************************************************ + */ + +start_acl_v3: acl_list + ; + +acl_list: acl + | acl_list acl + ; + +acl: named_acl ';' body_list + | named_acl ';' + ; + +named_acl: ACL_ACL_TOK ACL_VARIABLE_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>2); + free($<string>2); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + } + | ACL_ACL_TOK ACL_QSTRING_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>2); + free($<string>2); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + } + ; + +body_list: body + | body body_list + ; + +body: authenticate_statement ';' + | authorization_statement ';' + | deny_statement ';' + ; + +deny_statement: + ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set deny processing flags"); + return(-1); + } + } + deny_common + | ACL_DENY_TOK ACL_WITH_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + deny_common + ; + +deny_common: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprSetDenyWith(NULL, curr_expr, + $<string>1, $<string>3) < 0 ) { + yyerror("ACL_ExprSetDenyWith() failed"); + return(-1); + } + free($<string>1); + free($<string>3); + } + ; + +authenticate_statement: ACL_AUTHENTICATE_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + '(' attribute_list ')' '{' parameter_list '}' + { + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +attribute_list: attribute + | attribute_list ',' attribute + +attribute: ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprAddArg(NULL, curr_expr, $<string>1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free($<string>1); + } + ; + +parameter_list: parameter ';' + | parameter ';' parameter_list + ; + +parameter: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) { + } + free($<string>1); + } + | ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) { + } + free($<string>1); + } + ; + +authorization_statement: ACL_ALLOW_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + } + auth_common_action + | ACL_DENY_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + } + auth_common_action + ; + +auth_common_action: + { + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + auth_common + { + if ( ACL_ExprSetPFlags (NULL, curr_expr, pflags) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } +#ifdef DEBUG + if ( ACL_ExprDisplay(curr_expr) < 0 ) { + yyerror("ACL_ExprDisplay() failed"); + return(-1); + } + printf("Parsed authorization.\n"); +#endif + } + ; + +auth_common: flag_list '(' args_list ')' expression + ; + +flag_list: + | ACL_ABSOLUTE_TOK + { + pflags = ACL_PFLAG_ABSOLUTE; + } + | ACL_ABSOLUTE_TOK content_static + { + pflags = ACL_PFLAG_ABSOLUTE; + } + | ACL_CONTENT_TOK + { + pflags = ACL_PFLAG_CONTENT; + } + | ACL_CONTENT_TOK absolute_static + { + pflags = ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK + { + pflags = ACL_PFLAG_TERMINAL; + } + | ACL_TERMINAL_TOK content_absolute + { + pflags = ACL_PFLAG_TERMINAL; + } + ; + +content_absolute: ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_CONTENT; + } + | ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE; + } + | ACL_CONTENT_TOK ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } + | ACL_ABSOLUTE_TOK ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } + ; + +content_static: ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL; + } + | ACL_CONTENT_TOK ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } + ; + +absolute_static: ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE; + } + | ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL; + } + | ACL_ABSOLUTE_TOK ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } + | ACL_TERMINAL_TOK ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } + ; + +args_list: arg + | args_list ',' arg + ; + +arg: ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprAddArg(NULL, curr_expr, $<string>1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free( $<string>1 ); + } + ; + +expression: factor + | factor ACL_AND_TOK expression + { + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprAnd() failed"); + return(-1); + } + } + | factor ACL_OR_TOK expression + { + if ( ACL_ExprOr(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + ; + +factor: base_expr + | '(' expression ')' + | ACL_NOT_TOK factor + { + if ( ACL_ExprNot(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprNot() failed"); + return(-1); + } + } + ; + +base_expr: ACL_VARIABLE_TOK relop ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprTerm(NULL, curr_expr, + $<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($<string>1); + free($<string>3); + return(-1); + } + free($<string>1); + free($<string>3); + } + | ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprTerm(NULL, curr_expr, + $<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($<string>1); + free($<string>3); + return(-1); + } + free($<string>1); + free($<string>3); + } + ; + +relop: ACL_EQ_TOK + | ACL_GE_TOK + | ACL_GT_TOK + | ACL_LT_TOK + | ACL_LE_TOK + | ACL_NE_TOK + ; +%% diff --git a/lib/libaccess/acltools.cpp b/lib/libaccess/acltools.cpp new file mode 100644 index 00000000..1283147e --- /dev/null +++ b/lib/libaccess/acltools.cpp @@ -0,0 +1,3457 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Tools to build and maintain access control lists. + */ + +#include <stdio.h> +#include <string.h> + +#define ALLOCATE_ATTR_TABLE 1 /* Include the table of PList names */ + +#include <netsite.h> +#include <base/plist.h> +#include <base/util.h> +#include <base/crit.h> +#include <base/file.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclerror.h> +#include <libaccess/symbols.h> +#include <libaccess/aclstruct.h> +#include <libaccess/las.h> + +#include "aclscan.h" +#include "parse.h" +#include "oneeval.h" + +#include <libaccess/authdb.h> + +static CRITICAL acl_parse_crit = NULL; + +/* + * Allocate a new ACL handle + * + * This function creates a new ACL structure that will be used for + * access control information. + * + * Input: + * tag Specifies an identifier name for the new ACL, or + * it may be NULL when no name is required. + * Returns: + * A new ACL structure. + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_AclNew(NSErr_t *errp, char *tag ) +{ +ACLHandle_t *handle; + + handle = ( ACLHandle_t * ) PERM_CALLOC ( 1 * sizeof (ACLHandle_t) ); + if ( handle && tag ) { + handle->tag = PERM_STRDUP( tag ); + if ( handle->tag == NULL ) { + PERM_FREE(handle); + return(NULL); + } + } + return(handle); +} + +/* + * Appends to a specified ACL + * + * This function appends a specified ACL to the end of a given ACL list. + * + * Input: + * errp The error stack + * flags should always be zero now + * acl_list target ACL list + * acl new acl + * Returns: + * < 0 failure + * > 0 The number of acl's in the current list + */ + +NSAPI_PUBLIC int +ACL_ExprAppend( NSErr_t *errp, ACLHandle_t *acl, + ACLExprHandle_t *expr ) +{ + + if ( acl == NULL || expr == NULL ) + return(ACLERRUNDEF); + + expr->acl_tag = acl->tag; + + if ( expr->expr_type == ACL_EXPR_TYPE_AUTH || + expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + expr->expr_number = -1; // expr number isn't valid + } else { + acl->expr_count++; + expr->expr_number = acl->expr_count; + } + + if ( acl->expr_list_head == NULL ) { + acl->expr_list_head = expr; + acl->expr_list_tail = expr; + } else { + acl->expr_list_tail->expr_next = expr; + acl->expr_list_tail = expr; + } + + return(acl->expr_count); +} + +/* + * Add authentication information to an ACL + * + * This function adds authentication data to an expr, based on + * the information provided by the parameters. + * + * Input: + * expr an authenticate expression to add database + * and method information to. ie, auth_info + * auth_info authentication information, eg database, + * method, etc. + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprAddAuthInfo( ACLExprHandle_t *expr, PList_t auth_info ) +{ + if ( expr == NULL || auth_info == NULL ) + return(ACLERRUNDEF); + + expr->expr_auth = auth_info; + + return(0); +} + +/* + * Add authorization information to an ACL + * + * This function adds an authorization to a given ACL, based on the information + * provided by the parameters. + * + * Input: + * errp The error stack + * access_rights strings which identify the access rights to be + * controlled by the generated expr. + * flags processing flags + * allow non-zero to allow the indicated rights, or zero to + * deny them. + * attr_expr handle for an attribute expression, which may be + * obtained by calling ACL_ExprNew() + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_AddPermInfo( NSErr_t *errp, ACLHandle_t *acl, + char **access_rights, + PFlags_t flags, + int allow, + ACLExprHandle_t *expr, + char *tag ) +{ + if ( acl == NULL || expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags = flags; + expr->expr_argv = (char **) access_rights; + expr->expr_tag = PERM_STRDUP( tag ); + if ( expr->expr_tag == NULL ) + return(ACLERRNOMEM); + return(ACL_ExprAppend( errp, acl, expr )); +} + +/* + * Add rights information to an expression + * + * This function adds a right to an authorization, based on the information + * provided by the parameters. + * + * Input: + * errp The error stack + * access_right strings which identify the access rights to be + * controlled by the generated expr. + * expr handle for an attribute expression, which may be + * obtained by calling ACL_ExprNew() + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprAddArg( NSErr_t *errp, + ACLExprHandle_t *expr, + char *arg ) +{ + + if ( expr == NULL ) + return(ACLERRUNDEF); + + if (expr->expr_argv == NULL) + expr->expr_argv = (char **) PERM_MALLOC( 2 * sizeof(char *) ); + else + expr->expr_argv = (char **) PERM_REALLOC( expr->expr_argv, + (expr->expr_argc+2) + * sizeof(char *) ); + + if (expr->expr_argv == NULL) + return(ACLERRNOMEM); + + expr->expr_argv[expr->expr_argc] = PERM_STRDUP( arg ); + if (expr->expr_argv[expr->expr_argc] == NULL) + return(ACLERRNOMEM); + expr->expr_argc++; + expr->expr_argv[expr->expr_argc] = NULL; + + return(0); + +} + + +NSAPI_PUBLIC int +ACL_ExprSetDenyWith( NSErr_t *errp, ACLExprHandle_t *expr, char *deny_type, char *deny_response) +{ +int rv; + + if ( expr->expr_argc == 0 ) { + if ( (rv = ACL_ExprAddArg(errp, expr, deny_type)) < 0 ) + return(rv); + if ( (rv = ACL_ExprAddArg(errp, expr, deny_response)) < 0 ) + return(rv); + } else if ( expr->expr_argc == 2 ) { + if ( deny_type ) { + if ( expr->expr_argv[0] ) + PERM_FREE(expr->expr_argv[0]); + expr->expr_argv[0] = PERM_STRDUP(deny_type); + if ( expr->expr_argv[0] == NULL ) + return(ACLERRNOMEM); + } + if ( deny_response ) { + if ( expr->expr_argv[1] ) + PERM_FREE(expr->expr_argv[1]); + expr->expr_argv[1] = PERM_STRDUP(deny_response); + if ( expr->expr_argv[0] == NULL ) + return(ACLERRNOMEM); + } + } else { + return(ACLERRINTERNAL); + } + return(0); +} + +NSAPI_PUBLIC int +ACL_ExprGetDenyWith( NSErr_t *errp, ACLExprHandle_t *expr, char **deny_type, +char **deny_response) +{ + if ( expr->expr_argc == 2 ) { + *deny_type = expr->expr_argv[0]; + *deny_response = expr->expr_argv[1]; + return(0); + } else { + return(ACLERRUNDEF); + } +} + +/* + * Function to set the authorization statement processing flags. + * + * Input: + * errp The error reporting stack + * expr The authoization statement + * flags The flags to set + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprSetPFlags( NSErr_t *errp, + ACLExprHandle_t *expr, + PFlags_t flags ) +{ + if ( expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags |= flags; + return(0); +} + +/* + * Function to clear the authorization statement processing flags. + * + * Input: + * errp The error reporting stack + * expr The authoization statement + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprClearPFlags( NSErr_t *errp, + ACLExprHandle_t *expr ) +{ + if ( expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags = 0; + return(0); +} + +/* + * Allocate a new expression handle. + * + * Returns: + * NULL If handle could not be allocated. + * pointer New handle. + */ + +NSAPI_PUBLIC ACLExprHandle_t * +ACL_ExprNew( const ACLExprType_t expr_type ) +{ +ACLExprHandle_t *expr_handle; + + expr_handle = ( ACLExprHandle_t * ) PERM_CALLOC ( sizeof(ACLExprHandle_t) ); + if ( expr_handle ) { + expr_handle->expr_arry = ( ACLExprEntry_t * ) + PERM_CALLOC( ACL_TERM_BSIZE * sizeof(ACLExprEntry_t) ) ; + expr_handle->expr_arry_size = ACL_TERM_BSIZE; + expr_handle->expr_type = expr_type; + + expr_handle->expr_raw = ( ACLExprRaw_t * ) + PERM_CALLOC( ACL_TERM_BSIZE * sizeof(ACLExprRaw_t) ) ; + expr_handle->expr_raw_size = ACL_TERM_BSIZE; + + } + return(expr_handle); +} + + +/* + * LOCAL FUNCTION + * + * displays the ASCII equivalent index value. + */ + +static char * +acl_index_string ( int value, char *buffer ) +{ + + if ( value == ACL_TRUE_IDX ) { + strcpy( buffer, "TRUE" ); + return( buffer ); + } + + if ( value == ACL_FALSE_IDX ) { + strcpy( buffer, "FALSE" ); + return( buffer ); + } + + sprintf( buffer, "goto %d", value ); + return( buffer ); +} + + +/* + * LOCAL FUNCTION + * + * displays ASCII equivalent of CmpOp_t + */ + +static char * +acl_comp_string( CmpOp_t cmp ) +{ + switch (cmp) { + case CMP_OP_EQ: + return("="); + case CMP_OP_NE: + return("!="); + case CMP_OP_GT: + return(">"); + case CMP_OP_LT: + return("<"); + case CMP_OP_GE: + return(">="); + case CMP_OP_LE: + return("<="); + default: + return("unknown op"); + } +} + +/* + * Add a term to the specified attribute expression. + * + * Input: + * errp Error stack + * acl_expr Target expression handle + * attr_name Term Attribute name + * cmp Comparison operator + * attr_pattern Pattern for comparison + * Ouput: + * acl_expr New term added + * Returns: + * 0 Success + * < 0 Error + */ + +NSAPI_PUBLIC int +ACL_ExprTerm( NSErr_t *errp, ACLExprHandle_t *acl_expr, + char *attr_name, + CmpOp_t cmp, + char *attr_pattern ) +{ +ACLExprEntry_t *expr; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL || acl_expr->expr_arry == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_term_index >= acl_expr->expr_arry_size ) { + acl_expr->expr_arry = ( ACLExprEntry_t *) + PERM_REALLOC ( acl_expr->expr_arry, + (acl_expr->expr_arry_size + ACL_TERM_BSIZE) + * sizeof(ACLExprEntry_t)); + if ( acl_expr->expr_arry == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_arry_size += ACL_TERM_BSIZE; + } + + expr = &acl_expr->expr_arry[acl_expr->expr_term_index]; + acl_expr->expr_term_index++; + + expr->attr_name = PERM_STRDUP(attr_name); + if ( expr->attr_name == NULL ) + return(ACLERRNOMEM); + expr->comparator = cmp; + expr->attr_pattern = PERM_STRDUP(attr_pattern); + if ( expr->attr_pattern == NULL ) + return(ACLERRNOMEM); + expr->true_idx = ACL_TRUE_IDX; + expr->false_idx = ACL_FALSE_IDX; + expr->start_flag = 1; + expr->las_cookie = 0; + expr->las_eval_func = 0; + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t)); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->attr_name = expr->attr_name; + raw_expr->comparator = cmp; + raw_expr->attr_pattern = expr->attr_pattern; + raw_expr->logical = (ACLExprOp_t)0; + +#ifdef DEBUG_LEVEL_2 + printf ( "%d: %s %s %s, t=%d, f=%d\n", + acl_expr->expr_term_index - 1, + expr->attr_name, + acl_comp_string( expr->comparator ), + expr->attr_pattern, + expr->true_idx, + expr->false_idx ); +#endif + + return(0); +} + +/* + * Negate the previous term or subexpression. + * + * Input: + * errp The error stack + * acl_expr The expression to negate + * Ouput + * acl_expr The negated expression + * Returns: + * 0 Success + * < 0 Failure + */ + +NSAPI_PUBLIC int +ACL_ExprNot( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = 0; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t)); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_NOT; + raw_expr->attr_name = NULL; + + /* Find the last expression */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + expr_one = ii; + break; + } + } + +#ifdef DEBUG_LEVEL_2 + printf("not, start index=%d\n", expr_one); +#endif + + + /* + * The intent here is negate the last expression by + * modifying the true and false links. + */ + + for ( ii = expr_one; ii < acl_expr->expr_term_index; ii++ ) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].true_idx = ACL_FALSE_IDX; + else if ( acl_expr->expr_arry[ii].true_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].true_idx = ACL_TRUE_IDX; + + if ( acl_expr->expr_arry[ii].false_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].false_idx = ACL_FALSE_IDX; + else if ( acl_expr->expr_arry[ii].false_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].false_idx = ACL_TRUE_IDX; + + } + + return(0) ; +} + +/* + * Logical 'and' the previous two terms or subexpressions. + * + * Input: + * errp The error stack + * acl_expr The terms or subexpressions + * Output: + * acl_expr The expression after logical 'and' + */ + +NSAPI_PUBLIC int +ACL_ExprAnd( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = ACL_FALSE_IDX; +int expr_two = ACL_FALSE_IDX; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t) ); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_AND; + raw_expr->attr_name = NULL; + + /* Find the last two expressions */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + if ( expr_two == ACL_FALSE_IDX ) + expr_two = ii; + else if ( expr_one == ACL_FALSE_IDX ) { + expr_one = ii; + break; + } + } + } + +#ifdef DEBUG_LEVEL_2 + printf("and, index=%d, first expr=%d, second expr=%d\n", idx, expr_one, expr_two); +#endif + + for ( ii = expr_one; ii < expr_two; ii++) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].true_idx = expr_two; + if ( acl_expr->expr_arry[ii].false_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].false_idx = expr_two; + } + + acl_expr->expr_arry[expr_two].start_flag = 0; + return(0); +} + +/* + * Logical 'or' the previous two terms or subexpressions. + * + * Input: + * errp The error stack + * acl_expr The terms or subexpressions + * Output: + * acl_expr The expression after logical 'or' + */ + +NSAPI_PUBLIC int +ACL_ExprOr( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = ACL_FALSE_IDX; +int expr_two = ACL_FALSE_IDX; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t) ); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_OR; + raw_expr->attr_name = NULL; + + /* Find the last two expressions */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + if ( expr_two == ACL_FALSE_IDX ) + expr_two = ii; + else if ( expr_one == ACL_FALSE_IDX ) { + expr_one = ii; + break; + } + } + } + +#ifdef DEBUG_LEVEL_2 + printf("or, index=%d, first expr=%d, second expr=%d\n", idx, expr_one, expr_two); +#endif + + for ( ii = expr_one; ii < expr_two; ii++) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].true_idx = expr_two; + if ( acl_expr->expr_arry[ii].false_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].false_idx = expr_two; + } + acl_expr->expr_arry[expr_two].start_flag = 0; + + return(0); +} + +/* + * INTERNAL FUNCTION (GLOBAL) + * + * Write an expression array to standard output. This + * is only useful debugging. + */ + +int +ACL_ExprDisplay( ACLExprHandle_t *acl_expr ) +{ +int ii; +char buffer[256]; + + if ( acl_expr == NULL ) + return(0); + + for ( ii = 0; ii < acl_expr->expr_term_index; ii++ ) { + printf ("%d: if ( %s %s %s ) ", + ii, + acl_expr->expr_arry[ii].attr_name, + acl_comp_string( acl_expr->expr_arry[ii].comparator ), + acl_expr->expr_arry[ii].attr_pattern ); + + printf("%s ", acl_index_string(acl_expr->expr_arry[ii].true_idx, buffer)); + printf("else %s\n", + acl_index_string(acl_expr->expr_arry[ii].false_idx, buffer) ); + } + + return(0); +} + +/* + * Creates a handle for a new list of ACLs + * + * This function creates a new list of ACLs. The list is initially empty + * and can be added to by ACL_ListAppend(). A resource manager would use + * these functions to build up a list of all the ACLs applicable to a + * particular resource access. + * + * Input: + * Returns: + * NULL failure, otherwise returns a new + * ACLListHandle + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ListNew(NSErr_t *errp) +{ +ACLListHandle_t *handle; + + handle = ( ACLListHandle_t * ) PERM_CALLOC ( sizeof(ACLListHandle_t) ); + handle->ref_count = 1; + return(handle); +} + +/* + * Allocates a handle for an ACL wrapper + * + * This wrapper is just used for ACL list creation. It's a way of + * linking ACLs into a list. This is an internal function. + */ + +static ACLWrapper_t * +acl_wrapper_new(void) +{ +ACLWrapper_t *handle; + + handle = ( ACLWrapper_t * ) PERM_CALLOC ( sizeof(ACLWrapper_t) ); + return(handle); +} + +/* + * Description + * + * This function destroys an entry a symbol table entry for an + * ACL. + * + * Arguments: + * + * sym - pointer to Symbol_t for an ACL entry + * argp - unused (must be zero) + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +static +int acl_hash_entry_destroy(Symbol_t * sym, void * argp) +{ + if (sym != 0) { + + /* Free the acl name string if any */ + if (sym->sym_name != 0) { + PERM_FREE(sym->sym_name); + } + + /* Free the Symbol_t structure */ + PERM_FREE(sym); + } + + /* Indicate that the symbol table entry should be removed */ + return SYMENUMREMOVE; +} + + +/* + * LOCAL FUNCTION + * + * Create a new symbol with the sym_name equal to the + * acl->tag value. Attaches the acl to the sym_data + * pointer. + */ + +static Symbol_t * +acl_sym_new(ACLHandle_t *acl) +{ + Symbol_t *sym; + /* It's not there, so add it */ + sym = (Symbol_t *) PERM_MALLOC(sizeof(Symbol_t)); + if ( sym == NULL ) + return(NULL); + + sym->sym_name = PERM_STRDUP(acl->tag); + if ( sym->sym_name == NULL ) { + PERM_FREE(sym); + return(NULL); + } + + sym->sym_type = ACLSYMACL; + sym->sym_data = (void *) acl; + return(sym); + +} + +/* + * LOCAL FUNCTION + * + * Add a acl symbol to an acl_list's symbol table. + * + * Each acl list has a symbol table. the symbol table + * is a quick qay to reference named acl's + */ + +static int +acl_sym_add(ACLListHandle_t *acl_list, ACLHandle_t *acl) +{ +Symbol_t *sym; +int rv; + + if ( acl->tag == NULL ) + return(ACLERRUNDEF); + + rv = symTableFindSym(acl_list->acl_sym_table, + acl->tag, + ACLSYMACL, + (void **)&sym); + if ( rv == SYMERRNOSYM ) { + sym = acl_sym_new(acl); + if ( sym ) + rv = symTableAddSym(acl_list->acl_sym_table, sym, (void *)sym); + } + + if ( sym == NULL || rv < 0 ) + return(ACLERRUNDEF); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Destroy an acl_list's symbol table and all memory referenced + * by the symbol table. This does not destroy an acl_list. + */ + +static void +acl_symtab_destroy(ACLListHandle_t *acl_list) +{ + /* Destroy each entry in the symbol table */ + symTableEnumerate(acl_list->acl_sym_table, 0, acl_hash_entry_destroy); + /* Destory the hash table itself */ + symTableDestroy(acl_list->acl_sym_table, 0); + acl_list->acl_sym_table = NULL; + return; +} + + +/* + * Appends to a specified ACL + * + * This function appends a specified ACL to the end of a given ACL list. + * + * Input: + * errp The error stack + * flags should always be zero now + * acl_list target ACL list + * acl new acl + * Returns: + * > 0 The number of acl's in the current list + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListAppend( NSErr_t *errp, ACLListHandle_t *acl_list, ACLHandle_t *acl, + int flags ) +{ + ACLWrapper_t *wrapper; + ACLHandle_t *tmp_acl; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + if ( acl_list->acl_sym_table == NULL && + acl_list->acl_count == ACL_TABLE_THRESHOLD ) { + + /* + * The symbol table isn't really critical so we don't log + * an error if its creation fails. + */ + + symTableNew(&acl_list->acl_sym_table); + if ( acl_list->acl_sym_table ) { + for (wrapper = acl_list->acl_list_head; wrapper; + wrapper = wrapper->wrap_next ) { + tmp_acl = wrapper->acl; + if ( acl_sym_add(acl_list, tmp_acl) ) { + acl_symtab_destroy(acl_list); + break; + } + } + } + } + + wrapper = acl_wrapper_new(); + if ( wrapper == NULL ) + return(ACLERRNOMEM); + + wrapper->acl = acl; + + if ( acl_list->acl_list_head == NULL ) { + acl_list->acl_list_head = wrapper; + acl_list->acl_list_tail = wrapper; + } else { + acl_list->acl_list_tail->wrap_next = wrapper; + acl_list->acl_list_tail = wrapper; + } + + acl->ref_count++; + + acl_list->acl_count++; + + + if ( acl_list->acl_sym_table ) { + /* + * If we fail to insert the ACL then we + * might as well destroy this hash table since it is + * useless. + */ + if ( acl_sym_add(acl_list, acl) ) { + acl_symtab_destroy(acl_list); + } + } + + + return(acl_list->acl_count); +} + +/* + * Concatenates two ACL lists + * + * Attaches all ACLs in acl_list2 to the end of acl_list1. acl_list2 + * is left unchanged. + * + * Input: + * errp pointer to the error stack + * acl_list1 target ACL list + * acl_list2 source ACL list + * Output: + * acl_list1 list contains the concatenation of acl_list1 + * and acl_list2. + * Returns: + * > 0 Number of ACLs in acl_list1 after concat + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListConcat( NSErr_t *errp, ACLListHandle_t *acl_list1, + ACLListHandle_t *acl_list2, int flags ) +{ +ACLWrapper_t *wrapper; +int rv; + + if ( acl_list1 == NULL || acl_list2 == NULL ) + return(ACLERRUNDEF); + + for ( wrapper = acl_list2->acl_list_head; + wrapper != NULL; wrapper = wrapper->wrap_next ) + if ( (rv = ACL_ListAppend ( errp, acl_list1, wrapper->acl, 0 )) < 0 ) + return(rv); + + return(acl_list1->acl_count); +} + +/* + * LOCAL FUNCTION + * + * Free up memory associated with and ACLExprEntry. Probably + * only useful internally since we aren't exporting + * this structure. + */ + +static void +ACL_ExprEntryDestroy( ACLExprEntry_t *entry ) +{ + LASFlushFunc_t flushp; + + if ( entry == NULL ) + return; + + if ( entry->las_cookie ) +/* freeLAS(NULL, entry->attr_name, &entry->las_cookie); */ + { + ACL_LasFindFlush( NULL, entry->attr_name, &flushp ); + if ( flushp ) + ( *flushp )( &entry->las_cookie ); + } + + if ( entry->attr_name ) + PERM_FREE( entry->attr_name ); + + if ( entry->attr_pattern ) + PERM_FREE( entry->attr_pattern ); + + return; +} + +/* + * LOCAL FUNCTION + * + * This function is used to free all the pvalue memory + * in a plist. + */ + +static void +acl_expr_auth_destroy(char *pname, const void *pvalue, void *user_data) +{ + PERM_FREE((char *) pvalue); + return; +} + +/* + * Free up memory associated with and ACLExprHandle. + * + * Input: + * expr expression handle to free up + */ + +NSAPI_PUBLIC void +ACL_ExprDestroy( ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr == NULL ) + return; + + if ( expr->expr_tag ) + PERM_FREE( expr->expr_tag ); + + if ( expr->expr_argv ) { + for ( ii = 0; ii < expr->expr_argc; ii++ ) + if ( expr->expr_argv[ii] ) + PERM_FREE( expr->expr_argv[ii] ); + PERM_FREE( expr->expr_argv ); + } + + for ( ii = 0; ii < expr->expr_term_index; ii++ ) + ACL_ExprEntryDestroy( &expr->expr_arry[ii] ); + + if ( expr->expr_auth ) { + PListEnumerate(expr->expr_auth, acl_expr_auth_destroy, NULL); + PListDestroy(expr->expr_auth); + } + + PERM_FREE( expr->expr_arry ); + PERM_FREE( expr->expr_raw ); + + PERM_FREE( expr ); + + return; +} + +/* + * Free up memory associated with and ACLHandle. + * + * Input: + * acl target acl + */ + +NSAPI_PUBLIC void +ACL_AclDestroy(NSErr_t *errp, ACLHandle_t *acl ) +{ +ACLExprHandle_t *handle; +ACLExprHandle_t *tmp; + + if ( acl == NULL ) + return; + + acl->ref_count--; + + if ( acl->ref_count ) + return; + + if ( acl->tag ) + PERM_FREE( acl->tag ); + + if ( acl->las_name ) + PERM_FREE( acl->las_name ); + + if ( acl->attr_name ) + PERM_FREE( acl->attr_name ); + + handle = acl->expr_list_head; + while ( handle ) { + tmp = handle; + handle = handle->expr_next; + ACL_ExprDestroy( tmp ); + } + + PERM_FREE(acl); + + return; +} + +/* + * Destorys a input ACL List + * + * Input: + * acl_list target list + * Output: + * none target list is freed + */ + +NSAPI_PUBLIC void +ACL_ListDestroy(NSErr_t *errp, ACLListHandle_t *acl_list ) +{ + ACLWrapper_t *wrapper; + ACLWrapper_t *tmp_wrapper; + ACLHandle_t *tmp_acl; + + + if ( acl_list == NULL ) + return; + + if ( acl_list->acl_sym_table ) { + /* Destroy each entry in the symbol table */ + symTableEnumerate(acl_list->acl_sym_table, 0, acl_hash_entry_destroy); + /* Destory the hash table itself */ + symTableDestroy(acl_list->acl_sym_table, 0); + } + + ACL_EvalDestroyContext( (ACLListCache_t *)acl_list->cache ); + + wrapper = acl_list->acl_list_head; + + while ( wrapper ) { + tmp_acl = wrapper->acl; + tmp_wrapper = wrapper; + wrapper = wrapper->wrap_next; + PERM_FREE( tmp_wrapper ); + ACL_AclDestroy(errp, tmp_acl ); + } + + PERM_FREE( acl_list ); + + return; +} + +/* + * FUNCTION: ACL_ListGetFirst + * + * DESCRIPTION: + * + * This function is used to start an enumeration of an + * ACLListHandle_t. It returns an ACLHandle_t* for the first + * ACL on the list, and initializes a handle supplied by the + * caller, which is used to track the current position in the + * enumeration. This function is normally used in a loop + * such as: + * + * ACLListHandle_t *acl_list = <some ACL list>; + * ACLHandle_t *cur_acl; + * ACLListEnum_t acl_enum; + * + * for (cur_acl = ACL_ListGetFirst(acl_list, &acl_enum); + * cur_acl != 0; + * cur_acl = ACL_ListGetNext(acl_list, &acl_enum)) { + * ... + * } + * + * The caller should guarantee that no ACLs are added or removed + * from the ACL list during the enumeration. + * + * ARGUMENTS: + * + * acl_list - handle for the ACL list + * acl_enum - pointer to uninitialized enumeration handle + * + * RETURNS: + * + * As described above. If the acl_list argument is null, or the + * referenced ACL list is empty, the return value is null. + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListGetFirst(ACLListHandle_t *acl_list, ACLListEnum_t *acl_enum) +{ + ACLWrapper_t *wrapper; + ACLHandle_t *acl = 0; + + *acl_enum = 0; + + if (acl_list) { + + wrapper = acl_list->acl_list_head; + *acl_enum = (ACLListEnum_t)wrapper; + + if (wrapper) { + acl = wrapper->acl; + } + } + + return acl; +} + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListGetNext(ACLListHandle_t *acl_list, ACLListEnum_t *acl_enum) +{ + ACLWrapper_t *wrapper = (ACLWrapper_t *)(*acl_enum); + ACLHandle_t *acl = 0; + + if (wrapper) { + + wrapper = wrapper->wrap_next; + *acl_enum = (ACLListEnum_t)wrapper; + + if (wrapper) acl = wrapper->acl; + } + + return acl; +} + +/* + * FUNCTION: ACL_AclGetTag + * + * DESCRIPTION: + * + * Returns the tag string associated with an ACL. + * + * ARGUMENTS: + * + * acl - handle for an ACL + * + * RETURNS: + * + * The return value is a pointer to the ACL tag string. + */ + +NSAPI_PUBLIC const char * +ACL_AclGetTag(ACLHandle_t *acl) +{ + return (acl) ? (const char *)(acl->tag) : 0; +} + +/* + * Finds a named ACL in an input list. + * + * Input: + * acl_list a list of ACLs to search + * acl_name the name of the ACL to find + * flags e.g. ACL_CASE_INSENSITIVE + * Returns: + * NULL No ACL found + * acl A pointer to an ACL with named acl_name + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListFind (NSErr_t *errp, ACLListHandle_t *acl_list, char *acl_name, int flags ) +{ +ACLHandle_t *result = NULL; +ACLWrapper_t *wrapper; +Symbol_t *sym; + + if ( acl_list == NULL || acl_name == NULL ) + return( result ); + + /* + * right now the symbol table exists if there hasn't been + * any collisions based on using case insensitive names. + * if there are any collisions then the table will be + * deleted and we will look up using list search. + * + * we should probably create two hash tables, one for case + * sensitive lookups and the other for insensitive. + */ + if ( acl_list->acl_sym_table ) { + if ( symTableFindSym(acl_list->acl_sym_table, + acl_name, ACLSYMACL, (void **) &sym) >= 0 ) { + result = (ACLHandle_t *) sym->sym_data; + if ( result && (flags & ACL_CASE_SENSITIVE) && + strcmp(result->tag, acl_name) ) { + result = NULL; /* case doesn't match */ + } + } + return( result ); + } + + if ( flags & ACL_CASE_INSENSITIVE ) { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcasecmp( wrapper->acl->tag, acl_name ) == 0 ) { + result = wrapper->acl; + break; + } + } + } else { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcmp( wrapper->acl->tag, acl_name ) == 0 ) { + result = wrapper->acl; + break; + } + } + } + + return( result ); +} + +/* + * Function parses an input ACL file and resturns an + * ACLListHandle_t pointer that represents the entire + * file without the comments. + * + * Input: + * filename the name of the target ACL text file + * errp a pointer to an error stack + * + * Returns: + * NULL parse failed + * + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ParseFile( NSErr_t *errp, char *filename ) +{ +ACLListHandle_t *handle = NULL; +int eid = 0; +int rv = 0; +char *errmsg; + + ACL_InitAttr2Index(); + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + if ( acl_InitScanner( errp, filename, NULL ) < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } else { + + handle = ACL_ListNew(errp); + if ( handle == NULL ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_PushListHandle( handle ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_Parse() ) { + rv = ACLERRPARSE; + eid = ACLERR1780; + } + + if ( acl_EndScanner() < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1500; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } + + } + + if ( rv || eid ) { + ACL_ListDestroy(errp, handle); + handle = NULL; + } + + crit_exit( acl_parse_crit ); + return(handle); + +} + +/* + * Function parses an input ACL string and returns an + * ACLListHandle_t pointer that represents the entire + * file without the comments. + * + * Input: + * buffer the target ACL buffer + * errp a pointer to an error stack + * + * Returns: + * NULL parse failed + * + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ParseString( NSErr_t *errp, char *buffer ) +{ +ACLListHandle_t *handle = NULL; +int eid = 0; +int rv = 0; +char *errmsg; + + ACL_InitAttr2Index(); + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + if ( acl_InitScanner( errp, NULL, buffer ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else { + + handle = ACL_ListNew(errp); + if ( handle == NULL ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_PushListHandle( handle ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_Parse() ) { + rv = ACLERRPARSE; + eid = ACLERR1780; + } + + if ( acl_EndScanner() < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1500; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, "buffer", errmsg); + } + + } + + if ( rv || eid ) { + ACL_ListDestroy(errp, handle); + handle = NULL; + } + + crit_exit( acl_parse_crit ); + return(handle); + +} + +/* + * LOCAL FUNCTION + * + * Convert sub-expression to string. + */ + +static int +acl_expr_string( ACLExprOp_t logical, ACLExprStack_t *expr_stack ) +{ +char **expr_text; +char **prev_expr_text; +char *tmp; + + switch (logical) { + case ACL_EXPR_OP_NOT: + if ( expr_stack->stack_index < 1 ) { + printf("expression stack underflow.\n"); + return(ACLERRINTERNAL); + } + + expr_text = &expr_stack->expr_text[expr_stack->stack_index - 1]; + tmp = (char *) PERM_MALLOC(strlen(*expr_text) + 7); + if ( tmp == NULL ) + return(ACLERRNOMEM); + + if ( expr_stack->found_subexpression ) { + sprintf(tmp, "not (%s)", *expr_text); + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = expr_stack->stack_index - 1; + } else { + sprintf(tmp, "not %s", *expr_text); + } + + PERM_FREE(*expr_text); + *expr_text = tmp; + return(0); + + case ACL_EXPR_OP_AND: + case ACL_EXPR_OP_OR: + if ( expr_stack->stack_index < 2 ) { + printf("expression stack underflow.\n"); + return(ACLERRINTERNAL); + } + + expr_stack->stack_index--; + prev_expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + expr_stack->stack_index--; + expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + + tmp = (char *) PERM_MALLOC (strlen(*expr_text) + + strlen(*prev_expr_text) + 15); + if ( tmp == NULL ) + return(ACLERRNOMEM); + + if ( expr_stack->found_subexpression && + expr_stack->stack_index == expr_stack->last_subexpression && + logical == ACL_EXPR_OP_AND ) { + sprintf(tmp, "%s and\n (%s)", *expr_text, *prev_expr_text); + } else if ( expr_stack->found_subexpression && + expr_stack->stack_index == expr_stack->last_subexpression ) { + sprintf(tmp, "%s or\n (%s)", *expr_text, *prev_expr_text); + } else if ( logical == ACL_EXPR_OP_AND ) { + sprintf(tmp, "%s and\n %s", *expr_text, *prev_expr_text); + } else { + sprintf(tmp, "%s or\n %s", *expr_text, *prev_expr_text); + } + + expr_stack->found_subexpression++; + expr_stack->stack_index++; + PERM_FREE(*expr_text); + PERM_FREE(*prev_expr_text); + *expr_text = tmp; + *prev_expr_text = NULL; + return(0); + + default: + printf("Bad boolean logic value.\n"); + return(ACLERRINTERNAL); + } + +} + +/* + * LOCAL FUNCTION + * + * Reduce all sub-expressions to a single string. + */ + +static int +acl_reduce_expr_logic( ACLExprStack_t *expr_stack, ACLExprRaw_t *expr_raw ) +{ +char **expr_text; +char **prev_expr_text; +char *tmp; + + if (expr_raw->attr_name) { + if (expr_stack->stack_index >= ACL_EXPR_STACK ) { + printf("expression stack overflow."); + return(ACLERRINTERNAL); + } + + if ( expr_stack->found_subexpression && expr_stack->stack_index > 0 ) { + prev_expr_text = &expr_stack->expr_text[expr_stack->stack_index-1]; + tmp = (char *) PERM_MALLOC(strlen(*prev_expr_text) + 3); + sprintf(tmp, "(%s)", *prev_expr_text); + PERM_FREE(*prev_expr_text); + *prev_expr_text = tmp; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = expr_stack->stack_index - 1; + } + + expr_stack->expr[expr_stack->stack_index] = expr_raw; + expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + *expr_text = (char *) PERM_MALLOC(strlen(expr_raw->attr_name) + + strlen(expr_raw->attr_pattern) + + 7); + if ( *expr_text == NULL ) + return(ACLERRNOMEM); + + sprintf(*expr_text, "%s %s \"%s\"", expr_raw->attr_name, + acl_comp_string(expr_raw->comparator), + expr_raw->attr_pattern); + + expr_stack->stack_index++; + expr_stack->expr_text[expr_stack->stack_index] = NULL; + } else { + return(acl_expr_string(expr_raw->logical, expr_stack)); + } + return(0); +} + +/* + * LOCAL FUNCTION + * + * Appends str2 to str1. + * + * Input: + * str1 an existing dynamically allocated string + * str2 a text string + * Returns: + * 0 success + * < 0 failure + */ + +static int +acl_to_str_append(acl_string_t * p_aclstr, const char *str2) +{ + int str2len, newlen; + + if (p_aclstr == NULL || str2 == NULL) + return (ACLERRINTERNAL); + if (p_aclstr->str == NULL) { + p_aclstr->str = (char *) PERM_MALLOC(4096); + if (p_aclstr->str == NULL) + return (ACLERRNOMEM); + p_aclstr->str_size = 4096; + p_aclstr->str_len = 0; + } + + str2len = strlen(str2); + newlen = p_aclstr->str_len + str2len; + if (newlen >= p_aclstr->str_size) { + p_aclstr->str_size = str2len > 4095 ? str2len+p_aclstr->str_size+1 : 4096+p_aclstr->str_size ; + p_aclstr->str = (char *) PERM_REALLOC(p_aclstr->str, p_aclstr->str_size); + if (p_aclstr->str == NULL) + return (ACLERRNOMEM); + } + memcpy((void *)&(p_aclstr->str[p_aclstr->str_len]), (void *) str2, str2len+1); + p_aclstr->str_len += str2len; + return 0; +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression type either "Allow" or "Deny" + */ + +static int +acl_to_str_expr_type( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ + switch (expr->expr_type) { + case ACL_EXPR_TYPE_ALLOW: + acl_to_str_append(str_t, "allow "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_DENY: + acl_to_str_append(str_t, "deny "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_AUTH: + acl_to_str_append(str_t, "authenticate "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_RESPONSE: + acl_to_str_append(str_t, "deny with "); + return(0); + default: + return(ACLERRINTERNAL); + } +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression Rights "(right, right)" + */ + +static int +acl_to_str_expr_arg( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr->expr_argc <= 0 ) { + return(ACLERRINTERNAL); + } + + if ( expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + acl_to_str_append(str_t, expr->expr_argv[0]); + acl_to_str_append(str_t, "=\""); + acl_to_str_append(str_t, expr->expr_argv[1]); + acl_to_str_append(str_t, "\";\n"); + return(0); + } + + acl_to_str_append(str_t, "("); + for (ii = 0; ii < expr->expr_argc; ii++) { + acl_to_str_append(str_t, expr->expr_argv[ii]); + if ( ii < expr->expr_argc - 1 ) { + acl_to_str_append(str_t, ","); + } + } + acl_to_str_append(str_t, ") "); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Walks through the authentication statement PList_t and + * prints the structure to a string. + */ + +static void +acl_to_str_auth_expr(char *lval, const void *rval, void *user_data) +{ + // ###### char **str = (char **) user_data; + acl_string_t * p_aclstr = (acl_string_t *) user_data; + + acl_to_str_append(p_aclstr, "\t"); + acl_to_str_append(p_aclstr, lval); + acl_to_str_append(p_aclstr, " = \""); + acl_to_str_append(p_aclstr, (char *) rval); + acl_to_str_append(p_aclstr, "\";\n"); + + return; +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authencation statement to a string. + */ + +static int +acl_to_str_auth_logic( acl_string_t *str_t, ACLExprHandle_t *expr) +{ + + if ( expr->expr_auth == NULL ) { + acl_to_str_append(str_t, "{\n"); + acl_to_str_append(str_t, "# Authenticate statement with no body?\n"); + acl_to_str_append(str_t, "\tnull=null;\n"); + acl_to_str_append(str_t, "};\n"); + return(0); + } + + acl_to_str_append(str_t, "{\n"); + PListEnumerate(expr->expr_auth, acl_to_str_auth_expr, (void *) str_t); + acl_to_str_append(str_t, "};\n"); + + return(0); +} + + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authorization statement to a string. + */ + +static int +acl_to_str_expr_logic( acl_string_t *str_t, ACLExprHandle_t *expr, ACLExprStack_t *expr_stack) +{ +int rv = 0; +int ii; + + expr_stack->stack_index = 0; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = -1; + + for (ii = 0; ii < expr->expr_raw_index; ii++) { + rv = acl_reduce_expr_logic(expr_stack, &expr->expr_raw[ii]); + if (rv) break; + } + + if (!rv && expr_stack->expr_text[0]) { + acl_to_str_append(str_t, "\n "); + acl_to_str_append(str_t, expr_stack->expr_text[0]); + acl_to_str_append(str_t, ";\n"); + PERM_FREE(expr_stack->expr_text[0]); + } + + return(rv); +} + +/* + * LOCAL FUNCTION + * + * Output an ACL list to a string. + */ + +static int +acl_to_str_create( acl_string_t *str_t, ACLListHandle_t *acl_list ) +{ +ACLWrapper_t *wrap; +ACLHandle_t *acl; +ACLExprHandle_t *expr; +int rv = 0; +ACLExprStack_t *expr_stack; + + expr_stack = (ACLExprStack_t *) PERM_MALLOC(sizeof(ACLExprStack_t)); + if ( expr_stack == NULL ) + return(ACLERRNOMEM); + + acl_to_str_append(str_t, "# File automatically written\n"); + acl_to_str_append(str_t, "#\n"); + acl_to_str_append(str_t, "# You may edit this file by hand\n"); + acl_to_str_append(str_t, "#\n\n"); + if ( acl_list->acl_list_head == NULL ) { + PERM_FREE(expr_stack); + return(0); + } + + acl_to_str_append(str_t, "version 3.0;\n"); + for (wrap = acl_list->acl_list_head; wrap && !rv; + wrap = wrap->wrap_next ) { + acl = wrap->acl; + if ( acl->tag ) { + acl_to_str_append(str_t, "\nacl \""); + acl_to_str_append(str_t, acl->tag); + acl_to_str_append(str_t, "\";\n"); + } else { + acl_to_str_append(str_t, "\nacl;\n"); + } + + for (expr = acl->expr_list_head; expr && rv == 0; + expr = expr->expr_next ) { + + if ( (rv = acl_to_str_expr_type(str_t, expr)) < 0 ) + break; + + if ( (rv = acl_to_str_expr_arg(str_t, expr)) < 0) + break; + + switch (expr->expr_type) { + case ACL_EXPR_TYPE_DENY: + case ACL_EXPR_TYPE_ALLOW: + rv = acl_to_str_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_AUTH: + rv = acl_to_str_auth_logic(str_t, expr); + break; + case ACL_EXPR_TYPE_RESPONSE: + break; + } + + } + } + + PERM_FREE(expr_stack); + return(rv); +} + + +/* + * Creates an ACL text string from an ACL handle + * + * Input: + * errp error stack + * acl target text string pointer + * acl_list Source ACL list handle + * Ouput: + * acl a chunk of dynamic memory pointing to ACL text + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_WriteString(NSErr_t *errp, char **acl, ACLListHandle_t *acl_list) +{ + int rv; + acl_string_t str_t = {NULL,0,0}; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + rv = acl_to_str_create(&str_t, acl_list); + *acl = str_t.str; + + return ( rv ); +} + +/* + * Write an ACL text file from an input ACL list structure. + * + * Input: + * filename name for the output text file + * acl_list a list of ACLs to convert to text + * Output: + * errp an error stack, set if there are errors + * to report + * Returns: + * 0 success + * ACLERROPEN, + * ACLERRNOMEM on failure + */ + +NSAPI_PUBLIC int +ACL_WriteFile( NSErr_t *errp, char *filename, ACLListHandle_t *acl_list ) +{ +int rv; +int eid; +char *errmsg; +#ifdef UTEST +FILE *ofp; +#else +SYS_FILE ofp; +#endif +acl_string_t aclstr = {NULL,0,0}; +char *acl_text = NULL; + + if ( filename == NULL || acl_list == NULL ) { + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + return(ACLERROPEN); + } + +#ifdef UTEST + ofp = fopen(filename, "w"); + if ( ofp == NULL ) { +#else + ofp = system_fopenWT(filename); + if ( ofp == SYS_ERROR_FD ) { +#endif + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + return(ACLERROPEN); + } + + rv = acl_to_str_create(&aclstr, acl_list); + acl_text = aclstr.str; + + if ( rv ) { + eid = ACLERR3000; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else { +#ifdef UTEST + if (fputs(acl_text, ofp) == 0) { +#else + if (system_fwrite_atomic(ofp, acl_text, strlen(acl_text))==IO_ERROR) { +#endif + eid = ACLERR3200; + rv = ACLERRIO; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } + } + + if ( acl_text ) + PERM_FREE(acl_text); + +#ifdef UTEST + fclose(ofp); +#else + system_fclose(ofp); +#endif + + return(rv); +} + +/* + * Delete a named ACL from an ACL list + * + * Input: + * acl_list Target ACL list handle + * acl_name Name of the target ACL + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListAclDelete(NSErr_t *errp, ACLListHandle_t *acl_list, char *acl_name, int flags ) +{ +ACLHandle_t *acl = NULL; +ACLWrapper_t *wrapper; +ACLWrapper_t *wrapper_prev = NULL; +Symbol_t *sym; + + if ( acl_list == NULL || acl_name == NULL ) + return(ACLERRUNDEF); + + if ( flags & ACL_CASE_INSENSITIVE ) { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcasecmp( wrapper->acl->tag, acl_name ) == 0 ) { + acl = wrapper->acl; + break; + } + wrapper_prev = wrapper; + } + } else { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcmp( wrapper->acl->tag, acl_name ) == 0 ) { + acl = wrapper->acl; + break; + } + wrapper_prev = wrapper; + } + } + + if ( acl ) { + + if ( wrapper_prev ) { + wrapper_prev->wrap_next = wrapper->wrap_next; + } else { + acl_list->acl_list_head = wrapper->wrap_next; + } + + if ( acl_list->acl_list_tail == wrapper ) { + acl_list->acl_list_tail = wrapper_prev; + } + + acl = wrapper->acl; + acl_list->acl_count--; + PERM_FREE(wrapper); + + if ( acl_list->acl_sym_table ) { + if ( symTableFindSym(acl_list->acl_sym_table, + acl->tag, ACLSYMACL, (void **) &sym) < 0 ) { + + /* not found, this is an error of some sort */ + + } else { + symTableRemoveSym(acl_list->acl_sym_table, sym); + acl_hash_entry_destroy(sym, 0); + } + } + + ACL_AclDestroy(errp, acl); + return(0); + } + + return(ACLERRUNDEF); +} + +/* + * local function: translate string to lower case + * return <0: fail + * 0: succeed + */ +int +open_file_buf(FILE ** file, char * filename, char *mode, char ** buf, long * size) +{ + int rv = 0; + long cur = 0; + long in = 0; + struct stat fi; + + if (filename==NULL || mode==NULL) { + rv = ACLERROPEN; + goto open_cleanup; + } + + if ((*file=fopen(filename,mode))==NULL) { + rv = ACLERROPEN; + goto open_cleanup; + } + + if (system_stat(filename, &fi)==-1) { + rv = ACLERROPEN; + goto open_cleanup; + } + + *size = fi.st_size; + + if ((*buf=(char *)PERM_MALLOC(*size+1))==NULL) { + rv = ACLERRNOMEM; + goto open_cleanup; + } + + + rv = 0; + while (cur<*size) { + in=fread(&(*buf)[cur], 1, *size, *file); + cur = cur+in; + if (feof(*file)) { + break; + } + if (ferror(*file)) { + rv = ACLERRIO; + break; + } + } + if (rv==0) + (*buf)[cur] = 0; + +open_cleanup: + if (rv<0) { + if (*file) + fclose(*file); + if (*buf) + PERM_FREE(*buf); + } + return rv; +} + + +/* + * local function: writes buf to disk and close the file + */ +void +close_file_buf(FILE * file, char * filename, char * mode, char * buf) +{ + if (file==NULL) + return; + fclose(file); + if (strchr(mode, 'w')!=NULL || strchr(mode, 'a')!=NULL) { + file = fopen(filename, "wb"); + fwrite(buf,1,strlen(buf),file); + fclose(file); + } + PERM_FREE(buf); +} + + +/* + * local function: translate string to lower case + */ +char * +str_tolower(char * string) +{ + register char * p = string; + for (; *p; p++) + *p = tolower(*p); + return string; +} + +/* + * local function: get the first name appear in block + * return: 0 : not found, + * 1 : found + */ +int +acl_get_first_name(char * block, char ** name, char ** next) +{ + char bounds[] = "\t \"\';"; + char boundchar; + char *p=NULL, *q=NULL, *start=NULL, *end=NULL; + + if (block==NULL) + return 0; +try_next: + if ((p=strstr(block, "acl"))!=NULL) { + + // check if this "acl" is the first occurance in this line. + for (q=p-1; ((q>=block) && *q!='\n'); q--) { + if (strchr(" \t",*q)==NULL) { + // if not, try next; + block = p+3; + goto try_next; + } + } + + p+=3; + while (strchr(bounds,*p)&&(*p!=0)) + p++; + if (*p==0) + return 0; + boundchar = *(p-1); + start = p; + while ((boundchar!=*p)&&(*p!=0)&&(*p!=';')) + p++; + if (*p==0) + return 0; + end = p; + *name = (char *)PERM_MALLOC(end-start+1); + strncpy(*name, start, (end-start)); + (*name)[end-start]=0; + *next = end; + return 1; + } + return 0; +} + +/* + * local function: find the pointer to acl string from the given block + */ +char * +acl_strstr(char * block, char * aclname) +{ + const char set[] = "\t \"\';"; + char * name, * rstr = NULL; + char * lowerb = block; + int found = 0; + + if (block==NULL||aclname==NULL) + return NULL; + + while ((name = strstr(block, aclname))!=NULL && !found) { + if (name>lowerb) { // This should be true, just in case + if ((strchr(set,name[-1])!=0) && (strchr(set,name[strlen(aclname)])!=0)) { + // the other 2 sides are in boundary set, that means, this is an exact match. + while (&name[-1]>=lowerb) { + name --; + if (strchr(set, *name)==0) + break; // should point to 'l' + } + + if (name==lowerb) + return NULL; + + if ((name-2)>=lowerb) + if ((name[-2]=='a') && (name[-1]=='c') && (*name=='l')) { + name -= 2; // name point to 'a' + rstr = name; + while (TRUE) { + if (name==lowerb) { + found = 1; + break; + } + else if (name[-1]==' '||name[-1]=='\t') + name --; + else if (name[-1]=='\n') { + found = 1; + break; + } + else + break; // acl is not at the head, there are other chars. + } + } + } + block = name + strlen(aclname); + } + } + return rstr; +} + + + +/* + * local function: find the acl string from mapfile and return its acl structure + */ +int +get_acl_from_file(char * filename, char * aclname, ACLListHandle_t ** acllist_pp) +{ + int rv = 0; + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + long filesize; + FILE * file; + char * mirror=NULL, * text=NULL, *nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + + *acllist_pp = NULL; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL) { + rv = ACLERRUNDEF; + goto get_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 1))==NULL) { + rv = ACLERRNOMEM; + goto get_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + /* get the acl text from the mapfile */ + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto get_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto get_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + int len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if ((*acllist_pp=ACL_ParseString(NULL, text))==NULL) { + rv = ACLERRPARSE; + } + } + +get_cleanup: + if (pattern) + PERM_FREE(pattern); + if (file) + close_file_buf(file, filename, "rb", block); + if (mirror) + PERM_FREE(mirror); + if (text) + PERM_FREE(text); + if (nextname) + PERM_FREE(nextname); + return rv; +} + + +/* + * local function: delete the acl string from mapfile + */ +int +delete_acl_from_file(char * filename, char * aclname) +{ + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + int rv = ACLERRUNDEF; + long filesize; + FILE * file; + char * mirror=NULL, * text=NULL, * nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + int remain; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL) { + rv = ACLERRUNDEF; + goto delete_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto delete_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + /* file the acl text from the mapfile */ + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto delete_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto delete_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + int len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if (ACL_ParseString(NULL, text)==NULL) { + rv = ACLERRPARSE; + goto delete_cleanup; + } + } + + if (aclhead!=NULL) { // found the acl in the map file + + // int filesize = mpfile->Size(); + + remain = strlen(aclend); + if (memcpy(aclhead, aclend, remain)!=NULL) + rv = 0; + else + rv = ACLERRIO; + + aclhead[remain]=0; + + block = (char *) PERM_REALLOC(block, strlen(block)+1); + } + else + rv = ACLERRUNDEF; + +delete_cleanup: + if (pattern) + PERM_FREE(pattern); + if (text) + PERM_FREE(text); + if (mirror) + PERM_FREE(mirror); + if (nextname) + PERM_FREE(nextname); + if (file) + close_file_buf(file, filename, "wb", block); + return rv; +} + +/* + * local function: append the acl string to file + */ +int +append_acl_to_file(char * filename, char * aclname, char * acltext) +{ + int rv; + /* acltext has been parsed to verify syntax up to this point */ + char * pattern=NULL; + char * start=NULL; + char * block; + long filesize; + FILE * file; + long len; + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto append_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + } + + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto append_cleanup; + + // find the begining of acl, skip the version part + + len = strlen(block); + start = acl_strstr(acltext, pattern); + if ((block=(char *)PERM_REALLOC(block, len+strlen(start)+1))==NULL) { + rv = ACLERRNOMEM; + goto append_cleanup; + } + strcat(block, start); + +append_cleanup: + if (pattern) + PERM_FREE(pattern); + if (file) + close_file_buf(file, filename, "wb", block); + + return rv; +} + + + +/* + * local function: rename the acl name in the file + */ +int +rename_acl_in_file(char * filename, char * aclname, char * newname) +{ + ACLListHandle_t * racllist=NULL; + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + int rv = 0; + long filesize; + FILE * file; + int remain; + long len; + char * text=NULL, * mirror=NULL, * nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + char * cut=NULL; + acl_string_t str_t = {NULL,0,0}; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL || newname==NULL) { + rv = ACLERRUNDEF; + goto rename_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + // file the acl text from the mapfile + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto rename_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if ((racllist=ACL_ParseString(NULL, text))==NULL) { + rv = ACLERRPARSE; + goto rename_cleanup; + } + } + + if (aclhead!=NULL) { // found the acl in the map file + + remain = strlen(aclend); + // delete the acltext from where it is + if (memcpy(aclhead, aclend, remain)!=NULL) + rv = 0; + else + rv = ACLERRUNDEF; + + aclhead[remain] = 0; + len = strlen(block); + + /* establish the renamed the acl */ + acl_to_str_append(&str_t, "acl \""); + acl_to_str_append(&str_t, newname); + acl_to_str_append(&str_t, "\";"); + /* skip acl "..."; the semicollon in the last counts for the +1 + add the rest acl text to str_t */ + cut = strchr(text, ';'); // skip version ...; + cut = strchr(cut+1, ';') + 1; // skip acl ...; + if (cut==NULL) { + rv = ACLERRUNDEF; + goto rename_cleanup; + } + acl_to_str_append(&str_t, cut); + // acl_to_str_append(&str_t, "\n"); + + if ((block=(char *) PERM_REALLOC(block, len + strlen(str_t.str) + 1))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + // strcat(block, "\n"); + strcat(block, str_t.str); + } + else + rv = ACLERRUNDEF; + +rename_cleanup: + if (pattern) + PERM_FREE(pattern); + if (text) + PERM_FREE(text); + if (mirror) + PERM_FREE(mirror); + if (nextname) + PERM_FREE(nextname); + if (str_t.str) + PERM_FREE(str_t.str); + if (file) + close_file_buf(file, filename, "wb", block); + return rv; +} + + +/* + * Retrieves the definition of a named ACL + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * acl_text a dynmaically allocated text (result) + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * <0 failure + */ +NSAPI_PUBLIC int +ACL_FileGetAcl(NSErr_t *errp, + char *filename, + char *acl_name, + // ACLListHandle_t **acllist_p, + char ** acltext, + int flags) +{ + int rv; + ACLListHandle_t * acllist_p; + + if (acl_parse_crit == NULL) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = get_acl_from_file(filename, acl_name, &acllist_p); + + if (acllist_p == NULL) { + *acltext = NULL; + goto get_cleanup; + } + + /* + if ((rv=ACL_Decompose(errp, acltext, acllist_p))<0) { + *acltext = NULL; + goto get_cleanup; + } + */ + if ((rv=ACL_WriteString(errp, acltext, acllist_p))<0) { + *acltext = NULL; + goto get_cleanup; + } + + +get_cleanup: + + crit_exit( acl_parse_crit ); + + return rv; +} + + + +/* + * Delete a named ACL from an ACL file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileDeleteAcl(NSErr_t *errp, + char *filename, + char *acl_name, + int flags) +{ + int rv = 0; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = delete_acl_from_file(filename, acl_name); + + crit_exit( acl_parse_crit ); + return(rv); +} + + +/* + * Sets the definition of an ACL in an ACL file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * acl_text a string that defines the new ACL + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileSetAcl(NSErr_t *errp, + char *filename, + char *acl_text, + int flags) +{ + int rv = 0; + ACLListHandle_t *new_acl_list = NULL; + char **acl_name_list = NULL; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + // get the acl name. + new_acl_list = ACL_ParseString(errp, acl_text); + if ( new_acl_list == NULL ) { + rv = ACLERRPARSE; + goto set_cleanup; + } + + if ( ACL_ListGetNameList(errp, new_acl_list, &acl_name_list) < 0 ) { + rv = ACLERRNOMEM; + goto set_cleanup; + } + + + delete_acl_from_file(filename, acl_name_list[0]); + rv = append_acl_to_file(filename, acl_name_list[0], acl_text); + +set_cleanup: + + crit_exit( acl_parse_crit ); + if (new_acl_list) + ACL_ListDestroy(errp, new_acl_list); + if (acl_name_list) + free(acl_name_list); + return(rv); +} + + +/* + * Rename a named ACL in ACL text file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * new_acl_name New ACL name + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileRenameAcl(NSErr_t *errp, + char *filename, + char *aclname, + char *newname, + int flags) +{ + int rv = 0; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = rename_acl_in_file(filename, aclname, newname); + + crit_exit( acl_parse_crit ); + return(rv); + +} + + +// +// Merge a list of ACLs into one ACL +// +// Input: +// filename the target acl file +// acl_list ACLs to merge +// new_acl_name resultant ACL +// flags currently ignored +// Returns: +// 0 success +// < 0 failure +// + +NSAPI_PUBLIC int +ACL_FileMergeAcl(NSErr_t *errp, + char *filename, + char **acl_name_list, + char *new_acl_name, + int flags) +{ + ACLListHandle_t *new_acl_list = NULL; + ACLListHandle_t *tmp_acl_list = NULL; + int ii; + int rv; + ACLHandle_t *tmp_acl; + ACLHandle_t *new_acl; + ACLExprHandle_t *expr; + + + tmp_acl_list = ACL_ParseFile(errp, filename); + if ( tmp_acl_list == NULL ) { + rv = ACLERRPARSE; + goto cleanup; + } + + new_acl_list = ACL_ParseFile(errp, filename); + if ( new_acl_list == NULL ) { + rv = ACLERRPARSE; + goto cleanup; + } + + // first get rid of all the ACLs that will be merged + + for (ii = 0; acl_name_list[ii]; ii++) { + rv = ACL_ListAclDelete(errp, new_acl_list, acl_name_list[ii], flags); + if ( rv < 0 ) + goto cleanup; + } + + // now create ACL to house the merged result + new_acl = ACL_AclNew(errp, new_acl_name); + if ( new_acl == NULL ) { + rv = ACLERRNOMEM; + goto cleanup; + } + + rv = ACL_ListAppend(errp, new_acl_list, new_acl, flags); + if ( rv < 0 ) + goto cleanup; + + for (ii = 0; acl_name_list[ii]; ii++) { + tmp_acl = ACL_ListFind(errp, tmp_acl_list, acl_name_list[ii], flags); + if ( tmp_acl == NULL ) { + rv = ACLERRUNDEF; + goto cleanup; + } + for (expr = tmp_acl->expr_list_head; expr; expr = expr->expr_next) { + // This call can't really fail unless we pass it a NULL + // or some memory is corrupt. + rv = ACL_ExprAppend(errp, new_acl, expr); + if ( rv < 0 ) + goto cleanup; + tmp_acl->expr_list_head = expr->expr_next; + tmp_acl->expr_count--; + } + + // Last bit of clean up so the destroy routine isn't confused. + + tmp_acl->expr_list_tail = NULL; + tmp_acl->expr_count = 0; + } + + rv = ACL_WriteFile(errp, filename, new_acl_list); + +cleanup: + if ( new_acl_list ) + ACL_ListDestroy(errp, new_acl_list); + if ( tmp_acl_list ) + ACL_ListDestroy(errp, tmp_acl_list); + return(rv); +} + +// +// Merge a list of ACL files into one ACL file +// +// Input: +// filename the target acl file +// file_list ACL files to merge +// flags currently ignored +// Returns: +// 0 success +// < 0 failure +// + +NSAPI_PUBLIC int +ACL_FileMergeFile(NSErr_t *errp, + char *filename, + char **file_list, + int flags) +{ + ACLListHandle_t *new_acl_list = NULL; + ACLListHandle_t *tmp_acl_list = NULL; + int ii; + int rv; + + // we don't care if they have nothing to do + + if ( filename == NULL || file_list == NULL ) + return(0); + + new_acl_list = ACL_ListNew(errp); + if (new_acl_list == NULL) + return(ACLERRNOMEM); + + for (ii = 0; file_list[ii]; ii++) { + tmp_acl_list = ACL_ParseFile(errp, file_list[ii]); + if (tmp_acl_list == NULL) { + rv = ACLERRPARSE; + goto cleanup; + } + rv = ACL_ListConcat(errp, new_acl_list, tmp_acl_list, flags); + if ( rv < 0 ) + goto cleanup; + ACL_ListDestroy(errp, tmp_acl_list); + tmp_acl_list = NULL; + } + + rv = ACL_WriteFile(errp, filename, new_acl_list); + +cleanup: + if ( new_acl_list ) + ACL_ListDestroy(errp, new_acl_list); + if ( tmp_acl_list ) + ACL_ListDestroy(errp, tmp_acl_list); + return(rv); +} + +/* + * Destroy a NameList + * + * Input: + * name_list a dynamically allocated array of strings + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_NameListDestroy(NSErr_t *errp, char **name_list) +{ + int list_index; + + if ( name_list == NULL ) + return(ACLERRUNDEF); + + for ( list_index = 0; name_list[list_index]; list_index++ ) { + PERM_FREE(name_list[list_index]); + } + PERM_FREE(name_list); + return(0); +} + + +/* + * Gets a name list of consisting of all ACL names for input list. + * + * Input: + * acl_list an ACL List handle + * name_list pointer to a list of string pointers + * Returns: + * 0 success + * < 0 failure + */ +NSAPI_PUBLIC int +ACL_ListGetNameList(NSErr_t *errp, ACLListHandle_t *acl_list, char ***name_list) +{ + const int block_size = 50; + ACLWrapper_t *wrapper; + int list_index; + int list_size; + char **tmp_list; + char **local_list; + char *name; + + + if ( acl_list == NULL ) + return(ACLERRUNDEF); + + list_size = block_size; + local_list = (char **) PERM_MALLOC(sizeof(char *) * list_size); + if ( local_list == NULL ) + return(ACLERRNOMEM); + list_index = 0; + local_list[list_index] = NULL; + + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag ) + name = wrapper->acl->tag; + else + name = "noname"; + if ( list_index + 2 > list_size ) { + list_size += block_size; + tmp_list = (char **) PERM_REALLOC(local_list, + sizeof(char *) * list_size); + if ( tmp_list == NULL ) { + ACL_NameListDestroy(errp, local_list); + return(ACLERRNOMEM); + } + local_list = tmp_list; + } + local_list[list_index] = PERM_STRDUP(name); + if ( local_list[list_index] == NULL ) { + ACL_NameListDestroy(errp, local_list); + return(ACLERRNOMEM); + } + list_index++; + local_list[list_index] = NULL; + } + *name_list = local_list; + return(0); +} + +/* + * Gets a name list of consisting of all ACL names from the input aclfile + * + * Input: + * filename acl file + * name_list pointer to a list of string pointers + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileGetNameList(NSErr_t *errp, char * filename, char ***name_list) +{ + + const int block_size = 50; + int rv, list_size, list_index; + char ** local_list; + char * block ; + char * name; + char * next; + long filesize; + FILE * file; + char * head; + + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto list_cleanup; + + list_size = block_size; + local_list = (char **) PERM_MALLOC(sizeof(char *) * list_size); + if ( local_list == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + list_index = 0; + local_list[list_index] = NULL; + + head = block; + while ((acl_get_first_name(head, &name, &next))) { + + if (list_index+2 > list_size) { + list_size += block_size; + char ** tmp_list = (char **) PERM_REALLOC(local_list, sizeof(char *) * list_size); + if ( tmp_list == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + local_list = tmp_list; + } + // local_list[list_index] = PERM_STRDUP(name); + local_list[list_index] = name; + if ( local_list[list_index] == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + list_index++; + local_list[list_index] = NULL; + head = next; + } + + rv = 0; + *name_list = local_list; + +list_cleanup: + if (local_list && rv<0) + ACL_NameListDestroy(errp, local_list); + if (file) + close_file_buf(file, filename, "rb", block); + + return rv; +} + +/* + * Changes method to method plus DBTYPE, and registers + * databases. + * + * Input: + * errp error stack + * acl_list Target ACL list handle + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListPostParseForAuth(NSErr_t *errp, ACLListHandle_t *acl_list ) +{ + ACLHandle_t *acl; + ACLWrapper_t *wrap; + ACLExprHandle_t *expr; + char *method; + char *database; + int rv; + ACLDbType_t *dbtype; + ACLMethod_t *methodtype; + + if ( acl_list == NULL ) + return(0); + + for ( wrap = acl_list->acl_list_head; wrap; wrap = wrap->wrap_next ) { + + acl = wrap->acl; + if ( acl == NULL ) + continue; + + for ( expr = acl->expr_list_head; expr; expr = expr->expr_next ) { + + if ( expr->expr_type != ACL_EXPR_TYPE_AUTH || + expr->expr_auth == NULL) + continue; + + rv = PListGetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, + (void **) &method, NULL); + if ( rv >= 0 ) { + methodtype = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); + rv = ACL_MethodFind(errp, method, methodtype); + if (rv) { + nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, + 3, acl->tag, "method", method); + PERM_FREE(methodtype); + return(ACLERRUNDEF); + } + + rv = PListSetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, + methodtype, NULL); + if ( rv < 0 ) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, + 0); + return(ACLERRNOMEM); + } + PERM_FREE(method); + } + + rv = PListGetValue(expr->expr_auth, ACL_ATTR_DATABASE_INDEX, + (void **) &database, NULL); + + if (rv < 0) continue; + + /* The following function lets user use databases which are + * not registered by their administrators. This also fixes + * the backward compatibility. + */ + dbtype = (ACLDbType_t *)PERM_MALLOC(sizeof(ACLDbType_t)); + rv = ACL_RegisterDbFromACL(errp, (const char *) database, + dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, + 3, acl->tag, "database", database); + PERM_FREE(dbtype); + return(ACLERRUNDEF); + } + + rv = PListInitProp(expr->expr_auth, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE, + dbtype, NULL); + if ( rv < 0 ) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, + 0); + return(ACLERRNOMEM); + } + + } + + } + + return(0); + +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression Rights "right, right" + */ + +static int +acl_decompose_expr_arg( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr->expr_argc <= 0 ) { + return(ACLERRINTERNAL); + } + + if ( expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + acl_to_str_append(str_t, expr->expr_argv[0]); + acl_to_str_append(str_t, " \""); + acl_to_str_append(str_t, expr->expr_argv[1]); + acl_to_str_append(str_t, "\";\n"); + return(0); + } + + for (ii = 0; ii < expr->expr_argc; ii++) { + acl_to_str_append(str_t, expr->expr_argv[ii]); + if ( ii < expr->expr_argc - 1 ) { + acl_to_str_append(str_t, ","); + } + } + acl_to_str_append(str_t, ";\n"); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Walks through the authentication statement PList_t and + * prints the structure to a string. + */ + +static void +acl_decompose_auth_expr(char *lval, const void *rval, void *user_data) +{ + acl_string_t * p_aclstr = (acl_string_t *) user_data; + // #### + + acl_to_str_append(p_aclstr, " "); + acl_to_str_append(p_aclstr, lval); + acl_to_str_append(p_aclstr, "=\""); + acl_to_str_append(p_aclstr, (char *) rval); + acl_to_str_append(p_aclstr, "\""); + + return; +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authencation statement to a string. + */ + +static int +acl_decompose_auth_logic( acl_string_t * str_t, ACLExprHandle_t *expr) +{ + + if ( expr->expr_auth == NULL ) + return(0); + + acl_to_str_append(str_t, "exprs"); + PListEnumerate(expr->expr_auth, acl_decompose_auth_expr, (void *) str_t); + acl_to_str_append(str_t, ";\n"); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authorization statement to a string. + */ + +static int +acl_decompose_expr_logic( acl_string_t *str_t, ACLExprHandle_t *expr, ACLExprStack_t *expr_stack) +{ +int rv = 0; +int ii; + + expr_stack->stack_index = 0; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = -1; + + for (ii = 0; ii < expr->expr_raw_index; ii++) { + rv = acl_reduce_expr_logic(expr_stack, &expr->expr_raw[ii]); + if (rv) break; + } + + if (!rv && expr_stack->expr_text[0]) { + acl_to_str_append(str_t, "exprs "); + acl_to_str_append(str_t, expr_stack->expr_text[0]); + acl_to_str_append(str_t, ";\n"); + PERM_FREE(expr_stack->expr_text[0]); + } + + return(rv); +} + +static int +acl_decompose(acl_string_t *str_t, ACLListHandle_t *acl_list) +{ +ACLWrapper_t *wrap; +ACLHandle_t *acl; +ACLExprHandle_t *expr; +int rv = 0; +ACLExprStack_t *expr_stack; + + expr_stack = (ACLExprStack_t *) PERM_MALLOC(sizeof(ACLExprStack_t)); + if ( expr_stack == NULL ) + return(ACLERRNOMEM); + + if ( acl_list->acl_list_head == NULL ) { + PERM_FREE(expr_stack); + return(0); + } + + acl_to_str_append(str_t, "version 3.0;"); + for (wrap = acl_list->acl_list_head; wrap && !rv; + wrap = wrap->wrap_next ) { + acl = wrap->acl; + if ( acl->tag ) { + acl_to_str_append(str_t, "\nname \""); + acl_to_str_append(str_t, acl->tag); + acl_to_str_append(str_t, "\";\n"); + } else { + acl_to_str_append(str_t, "\nname;\n"); + } + + for (expr = acl->expr_list_head; expr && rv == 0; + expr = expr->expr_next ) { + + switch (expr->expr_type) { + case ACL_EXPR_TYPE_DENY: + acl_to_str_append(str_t, "type deny;\nrights "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_ALLOW: + acl_to_str_append(str_t, "type allow;\nrights "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_AUTH: + acl_to_str_append(str_t, "type authenticate;\nattrs "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_auth_logic(str_t, expr); + break; + case ACL_EXPR_TYPE_RESPONSE: + acl_to_str_append(str_t, "type response;\nattrs "); + rv = acl_decompose_expr_arg(str_t, expr); + break; + } + } + } + + PERM_FREE(expr_stack); + return(rv); +} + +/* + * Converts an ACLListHandle_t to a parameter list suitable for passing + * to the ACL UI. + * + * Input: + * errp error stack + * acl a pointer to a string, holds the result of the + * decomposition. + * acl_list Target ACL list handle + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_Decompose(NSErr_t *errp, char **acl, ACLListHandle_t *acl_list) +{ + int rv ; + acl_string_t aclstr={NULL,0,0}; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + rv = acl_decompose(&aclstr, acl_list); + *acl = aclstr.str; + + return ( rv ); +} + +/* + * The following routines are used to validate input parameters. They always + * return 1, or cause an NS_ASSERT failure. The proper way to use them is + * with an NS_ASSERT in the calling function. E.g. + * NS_ASSERT(ACL_AssertAcllist(acllist)); + */ + +int +ACL_AssertAcllist(ACLListHandle_t *acllist) +{ + ACLWrapper_t *wrap; + + if (acllist == ACL_LIST_NO_ACLS) return 1; + NS_ASSERT(acllist); + NS_ASSERT(acllist->acl_list_head); + NS_ASSERT(acllist->acl_list_tail); + NS_ASSERT(acllist->acl_count); + NS_ASSERT(acllist->ref_count > 0); + + for (wrap=acllist->acl_list_head; wrap; wrap=wrap->wrap_next) { + NS_ASSERT(ACL_AssertAcl(wrap->acl)); + } + + /* Artificially limit ACL lists to 10 ACLs for now */ + NS_ASSERT(acllist->acl_count < 10); + + return 1; +} + +int +ACL_AssertAcl(ACLHandle_t *acl) +{ + NS_ASSERT(acl); + NS_ASSERT(acl->ref_count); + NS_ASSERT(acl->expr_count); + NS_ASSERT(acl->expr_list_head); + NS_ASSERT(acl->expr_list_tail); + + return 1; +} + +static PList_t ACLAttr2IndexPList = NULL; + +int +ACL_InitAttr2Index(void) +{ + int i; + + if (ACLAttr2IndexPList) return 0; + + ACLAttr2IndexPList = PListNew(NULL); + for (i = 1; i < ACL_ATTR_INDEX_MAX; i++) { + PListInitProp(ACLAttr2IndexPList, NULL, ACLAttrTable[i], (const void *)i, NULL); + } + + return 0; +} + +/* + * Attempt to locate the index number for one of the known attribute names + * that are stored in plists. If we can't match it, just return 0. + */ +int +ACL_Attr2Index(const char *attrname) +{ + int index = 0; + + if ( ACLAttr2IndexPList ) { + PListFindValue(ACLAttr2IndexPList, attrname, (void **)&index, NULL); + if (index < 0) index = 0; + } + return index; +} diff --git a/lib/libaccess/aclutil.cpp b/lib/libaccess/aclutil.cpp new file mode 100644 index 00000000..8d30fffb --- /dev/null +++ b/lib/libaccess/aclutil.cpp @@ -0,0 +1,223 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Source file for the TimeOfDay and DayOfWeek LAS drivers + */ + +#include <netsite.h> +#include <base/crit.h> +/* #include <plhash.h> */ +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/las.h> +#include <libaccess/nserror.h> +#include "aclutil.h" + +/* Generic evaluator of comparison operators in attribute evaluation + * statements. + * INPUT + * CmpOp_t ACL_TOKEN_EQ, ACL_TOKEN_NE etc. + * result 0 if equal, >0 if real > pattern, <0 if + * real < pattern. + * RETURNS + * LAS_EVAL_TRUE or LAS_EVAL_FALSE or LAS_EVAL_INVALID + * DEBUG + * Can add asserts that the strcmp failure cases are one of the + * remaining legal comparators. + */ +int +evalComparator(CmpOp_t ctok, int result) +{ + if (result == 0) { + switch(ctok) { + case CMP_OP_EQ: + case CMP_OP_GE: + case CMP_OP_LE: + return LAS_EVAL_TRUE; + case CMP_OP_NE: + case CMP_OP_GT: + case CMP_OP_LT: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } else if (result > 0) { + switch(ctok) { + case CMP_OP_GT: + case CMP_OP_GE: + case CMP_OP_NE: + return LAS_EVAL_TRUE; + case CMP_OP_LT: + case CMP_OP_LE: + case CMP_OP_EQ: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } else { /* real < pattern */ + switch(ctok) { + case CMP_OP_LT: + case CMP_OP_LE: + case CMP_OP_NE: + return LAS_EVAL_TRUE; + case CMP_OP_GT: + case CMP_OP_GE: + case CMP_OP_EQ: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } +} + + +/* Takes a string and returns the same string with all uppercase +* letters converted to lowercase. +*/ +void +makelower(char *string) +{ + while (*string) { + *string = tolower(*string); + string++; + } +} + + +/* Given an LAS_EVAL_* value, translates to ACL_RES_* */ +int +EvalToRes(int value) +{ + switch (value) { + case LAS_EVAL_TRUE: + return ACL_RES_ALLOW; + case LAS_EVAL_FALSE: + return ACL_RES_DENY; + case LAS_EVAL_DECLINE: + return ACL_RES_FAIL; + case LAS_EVAL_FAIL: + return ACL_RES_FAIL; + case LAS_EVAL_INVALID: + return ACL_RES_INVALID; + case LAS_EVAL_NEED_MORE_INFO: + return ACL_RES_DENY; + default: + NS_ASSERT(1); + return ACL_RES_ERROR; + } +} + +const char *comparator_string (int comparator) +{ + static char invalid_cmp[32]; + + switch(comparator) { + case CMP_OP_EQ: return "CMP_OP_EQ"; + case CMP_OP_NE: return "CMP_OP_NE"; + case CMP_OP_GT: return "CMP_OP_GT"; + case CMP_OP_LT: return "CMP_OP_LT"; + case CMP_OP_GE: return "CMP_OP_GE"; + case CMP_OP_LE: return "CMP_OP_LE"; + default: + sprintf(invalid_cmp, "unknown comparator %d", comparator); + return invalid_cmp; + } +} + +/* Return the pointer to the next token after replacing the following 'delim' + * char with NULL. + * WARNING - Modifies the first parameter */ +char *acl_next_token (char **ptr, char delim) +{ + char *str = *ptr; + char *token = str; + char *comma; + + if (!token) { *ptr = 0; return 0; } + + /* ignore leading whitespace */ + while(*token && isspace(*token)) token++; + + if (!*token) { *ptr = 0; return 0; } + + if ((comma = strchr(token, delim)) != NULL) { + *comma++ = 0; + } + + { + /* ignore trailing whitespace */ + int len = strlen(token); + char *sptr = token+len-1; + + while(*sptr == ' ' || *sptr == '\t') *sptr-- = 0; + } + + *ptr = comma; + return token; +} + + +/* Returns a pointer to the next token and it's length */ +/* tokens are separated by 'delim' characters */ +/* ignores whitespace surrounding the tokens */ +const char *acl_next_token_len (const char *ptr, char delim, int *len) +{ + const char *str = ptr; + const char *token = str; + const char *comma; + + *len = 0; + + if (!token) { return 0; } + + /* ignore leading whitespace */ + while(*token && isspace(*token)) token++; + + if (!*token) { return 0; } + if (*token == delim) { return token; } /* str starts with delim! */ + + if ((comma = strchr(token, delim)) != NULL) { + *len = comma - token; + } + else { + *len = strlen(token); + } + + { + /* ignore trailing whitespace */ + const char *sptr = token + *len - 1; + + while(*sptr == ' ' || *sptr == '\t') { + sptr--; + (*len)--; + } + } + + return token; +} + +/* acl_get_req_time -- + * If the REQ_TIME is available on the 'resource' plist, return it. + * Otherwise, make a system call to get the time and insert the time on the + * 'resource' PList. Allocate the time_t structure using the 'resource' + * PList's pool. + */ +time_t *acl_get_req_time (PList_t resource) +{ + time_t *req_time = 0; + int rv = PListGetValue(resource, ACL_ATTR_TIME_INDEX, (void **)&req_time, + NULL); + + if (rv < 0) { + req_time = (time_t *)pool_malloc(PListGetPool(resource), sizeof(time_t)); + time(req_time); + PListInitProp(resource, ACL_ATTR_TIME_INDEX, ACL_ATTR_TIME, + (void *)req_time, NULL); + } + + return req_time; +} diff --git a/lib/libaccess/aclutil.h b/lib/libaccess/aclutil.h new file mode 100644 index 00000000..34a1d0a6 --- /dev/null +++ b/lib/libaccess/aclutil.h @@ -0,0 +1,25 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef ACLUTIL_H +#define ACLUTIL_H + +NSPR_BEGIN_EXTERN_C + +int evalComparator(CmpOp_t ctok, int result); +void makelower(char *string); +int EvalToRes(int value); +const char *comparator_string (int comparator); + +/*Warning: acl_next_token modifies 'ptr' */ +char *acl_next_token (char **ptr, char delim); + +const char *acl_next_token_len (const char *ptr, + char delim, int *len); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/attrec.cpp b/lib/libaccess/attrec.cpp new file mode 100644 index 00000000..8911e896 --- /dev/null +++ b/lib/libaccess/attrec.cpp @@ -0,0 +1,309 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (attrec.c) + * + * This module contains routines for encoding and decoding + * attribute records. See attrec.h for a description of attribute + * records. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_ATTREC +#include "libaccess/attrec.h" + +/* + * Description (NTS_Length) + * + * This function returns the length of a null-terminated string. + * The length includes the terminating null octet. + * + * Use of the NTSLENGTH() macro is recommended (see attrec.h). + * + * Arguments: + * + * nts - a pointer to the null-terminate string + * (may be null) + * + * Returns: + * + * The length of the string. If 'nts' is null, the value is one, + * since there is always a null octet. + */ + +int NTS_Length(NTS_t nts) +{ + return ((nts) ? strlen((const char *)nts) + 1 : 1); +} + +/* + * Description (NTS_Decode) + * + * This function decodes a null-terminated string from a specified + * attribute record buffer. It copies the string into a dynamically + * allocated buffer, if 'pnts' is not null, and returns a pointer + * to it. The return value of the function is a pointer to the + * octet following the NTS in the attribute record buffer. + * + * Use of the NTSDECODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * pnts - pointer to returned reference to decoded + * NTS, or null, if the decoded NTS is not + * to be copied to a dynamic buffer + * + * Returns: + * + * The function return value is a pointer to the octet following + * the NTS in the attribute record buffer. A pointer to a + * dynamically allocated buffer containing the decoded NTS will + * be returned through 'pnts', if it is non-null. This returned + * pointer will be null if the NTS contains only a terminating + * octet. + */ + +ATR_t NTS_Decode(ATR_t cp, NTS_t * pnts) +{ + NTS_t nts = 0; + int len = NTSLENGTH(cp); /* length of the string */ + + /* Are we going to return a copy of the string? */ + if (pnts) { + + /* Yes, is it more than just a null octet? */ + if (len > 1) { + + /* Yes, allocate a buffer and copy the string to it */ + nts = (NTS_t)MALLOC(len); + if (nts) { + memcpy((void *)nts, (void *)cp, len); + } + } + + /* Return a pointer to the copied string, or null */ + *pnts = nts; + } + + /* Return pointer to octet after string */ + return cp + len; +} + +/* + * Description (NTS_Encode) + * + * This function encodes a null-terminated string into a specified + * attribute record buffer. It returns a pointer to the octet + * following the encoding. + * + * Use of the NTSENCODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * nts - pointer to the string to be encoded + * + * Returns: + * + * A pointer to the octet following the encoding in the attribute + * record buffer is returned. + */ + +ATR_t NTS_Encode(ATR_t cp, NTS_t nts) +{ + + /* Is the string pointer null? */ + if (nts) { + int len = NTSLENGTH(nts); + + /* No, copy the string to the attribute record buffer */ + memcpy((void *)cp, (void *)nts, len); + + /* Get pointer to octet after it */ + cp += len; + } + else { + + /* A null pointer indicates an empty NTS, i.e. just a null octet */ + *cp++ = 0; + } + + /* Return a pointer to the octet after the encoding */ + return cp; +} + +/* + * Description (USI_Decode) + * + * This function decodes an unsigned integer value from a specified + * attribute record buffer. + * + * Use of the USIDECODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * pval - pointer to returned integer value + * + * Returns: + * + * If 'pval' is not null, the decoded integer value is returned + * in the referenced location. The function return value is a + * pointer to the octet following the USI encoding in the attribute + * record buffer. + */ + +ATR_t USI_Decode(ATR_t cp, USI_t * pval) +{ + int val; + + /* Is this a length value? */ + if (*(cp) & 0x80) { + int i; + int len; + + /* Yes, build the value from the indicated number of octets */ + len = *cp++ & 0x7; + val = 0; + for (i = 0; i < len; ++i) { + val <<= 8; + val |= (cp[i] & 0xff); + } + cp += len; + } + else { + + /* This octet is the value */ + val = *cp++; + } + + /* Return the value if there's a place to put it */ + if (pval) *pval = val; + + /* Return a pointer to the next item in the attribute record */ + return cp; +} + +/* + * Description (USI_Encode) + * + * This function encodes an unsigned integer value into a specified + * attribute record buffer. + * + * Use of the USIENCODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * val - the value to be encoded + * + * Returns: + * + * A pointer to the octet following the generated encoding in the + * attribute record buffer is returned. + */ + +ATR_t USI_Encode(ATR_t cp, USI_t val) +{ + /* Check size of value to be encoded */ + if (val <= 0x7f) *cp++ = val; + else if (val <= 0xff) { + /* Length plus 8-bit value */ + *cp++ = 0x81; + *cp++ = val; + } + else if (val <= 0xffff) { + /* Length plus 16-bit value */ + *cp++ = 0x82; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 2; + } + else if (val <= 0xffffff) { + /* Length plus 24-bit value */ + *cp++ = 0x83; + cp[2] = val & 0xff; + val >>= 8; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 3; + } + else { + /* Length plus 32-bit value */ + *cp++ = 0x84; + cp[3] = val & 0xff; + val >>= 8; + cp[2] = val & 0xff; + val >>= 8; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 4; + } + + /* Return a pointer to the next position in the attribute record */ + return cp; +} + +/* + * Description (USI_Insert) + * + * This function is a variation of USI_Encode() that always generates + * the maximum-length encoding for USI value, regardless of the + * actual specified value. For arguments, returns, see USI_Encode(). + * + * Use of the USIINSERT() macro is recommended. The USIALLOC() macro + * returns the number of octets that USIINSERT() will generate. + */ + +ATR_t USI_Insert(ATR_t cp, USI_t val) +{ + int i; + + assert(USIALLOC() == 5); + + *cp++ = 0x84; + for (i = 3; i >= 0; --i) { + cp[i] = val & 0xff; + val >>= 8; + } + + return cp + 5; +} + +/* + * Description (USI_Length) + * + * This function returns the number of octets required to encode + * an unsigned integer value. + * + * Use of the USILENGTH() macro is recommended (see attrec.h). + * + * Arguments: + * + * val - the unsigned integer value + * + * Returns: + * + * The number of octets required to encode the specified value is + * returned. + */ + +int USI_Length(USI_t val) +{ + return (((USI_t)(val) <= 0x7f) ? 1 + : (((USI_t)(val) <= 0xff) ? 2 + : (((USI_t)(val) <= 0xffff) ? 3 + : (((USI_t)(val) <= 0xffffff) ? 4 + : 5)))); +} + diff --git a/lib/libaccess/authdb.cpp b/lib/libaccess/authdb.cpp new file mode 100644 index 00000000..0d27635d --- /dev/null +++ b/lib/libaccess/authdb.cpp @@ -0,0 +1,339 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> + +#include <plhash.h> + +#include <netsite.h> +#include "permhash.h" +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> +#include <ldaputil/dbconf.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/authdb.h> +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <libaccess/acl.h> +#include <libaccess/aclglobal.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define BIG_LINE 1024 + +char *ACL_default_dbname = 0; +ACLDbType_t ACL_default_dbtype = ACL_DBTYPE_INVALID; +ACLMethod_t ACL_default_method = ACL_METHOD_INVALID; +int acl_registered_dbcnt = 0; + +extern int acl_registered_names(PLHashTable *ht, int count, char ***names); + +/************************** Database Types *************************/ + +#define databaseNamesHashTable ACLDbNameHash + +int acl_num_databases () +{ + return acl_registered_dbcnt; +} + +static int reg_dbname_internal (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist) +{ + DbParseFn_t parseFunc; + void *db; + int rv; + AuthdbInfo_t *authdb_info; + + if (!ACL_DbTypeIsRegistered(errp, dbtype)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4400, ACL_Program, 2, XP_GetAdminStr(DBT_DbtypeNotDefinedYet), dbname); + return -1; + } + + parseFunc = ACL_DbTypeParseFn(errp, dbtype); + + if (!parseFunc) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4400, ACL_Program, 2, XP_GetAdminStr(DBT_DbtypeNotDefinedYet), dbname); + return -1; + } + + rv = (*parseFunc)(errp, dbtype, dbname, url, plist, (void **)&db); + + if (rv < 0) { + /* plist contains error message/code */ + return rv; + } + + /* Store the db returned by the parse function in the hash table. + */ + + authdb_info = (AuthdbInfo_t *)pool_malloc(ACL_DATABASE_POOL, sizeof(AuthdbInfo_t)); + + if (!authdb_info) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4420, ACL_Program, 0); + return -1; + } + + authdb_info->dbname = pool_strdup(ACL_DATABASE_POOL, dbname); + authdb_info->dbtype = dbtype; + authdb_info->dbinfo = db; /* value returned from parseFunc */ + + PR_HashTableAdd(ACLDbNameHash, authdb_info->dbname, authdb_info); + acl_registered_dbcnt++; + + return 0; +} + +NSAPI_PUBLIC int ACL_DatabaseRegister (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist) +{ + if (!dbname || !*dbname) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4500, ACL_Program, 1, XP_GetAdminStr(DBT_DatabaseRegisterDatabaseNameMissing)); + return -1; + } + + return reg_dbname_internal(errp, dbtype, dbname, url, plist); +} + +NSAPI_PUBLIC int +ACL_DatabaseNamesGet(NSErr_t *errp, char ***names, int *count) +{ + *count = acl_registered_dbcnt; + return acl_registered_names (ACLDbNameHash, *count, names); +} + +NSAPI_PUBLIC int +ACL_DatabaseNamesFree(NSErr_t *errp, char **names, int count) +{ + int i; + + for (i = count-1; i; i--) FREE(names[i]); + + FREE(names); + return 0; +} + +/* try to determine the dbtype from the database url */ +static int acl_url_to_dbtype (const char *url, ACLDbType_t *dbtype_out) +{ + ACLDbType_t dbtype; + NSErr_t *errp = 0; + + *dbtype_out = dbtype = ACL_DBTYPE_INVALID; + if (!url || !*url) return -1; + + // urls with ldap:, ldaps: and ldapdb: are all of type ACL_DBTYPE_LDAP. + if (!strncmp(url, URL_PREFIX_LDAP, URL_PREFIX_LDAP_LEN)) + dbtype = ACL_DbTypeLdap; + else { + /* treat prefix in the url as dbtype if it has been registered. + */ + int prefix_len = strcspn(url, ":"); + char dbtypestr[BIG_LINE]; + + if (prefix_len) { + strncpy(dbtypestr, url, prefix_len); + dbtypestr[prefix_len] = 0; + + if (!ACL_DbTypeFind(errp, dbtypestr, &dbtype)) { + /* prefix is not a registered dbtype */ + dbtype = ACL_DBTYPE_INVALID; + } + } + } + + if (ACL_DbTypeIsEqual(errp, dbtype, ACL_DBTYPE_INVALID)) { + /* try all the registered parse functions to determine the dbtype */ + } + + if (ACL_DbTypeIsEqual(errp, dbtype, ACL_DBTYPE_INVALID)) return -1; + + *dbtype_out = dbtype; + return 0; +} + +NSAPI_PUBLIC int ACL_RegisterDbFromACL (NSErr_t *errp, const char *url, + ACLDbType_t *dbtype) +{ + /* If the database by name url is already registered, don't do anything. + * If it is not registered, determine the dbtype from the url. + * If the dbtype can be determined, register the database with dbname same + * as the url. Return the dbtype. + */ + void *db; + int rv; + PList_t plist; + + if (ACL_DatabaseFind(errp, url, dbtype, &db) == LAS_EVAL_TRUE) + return 0; + + /* The database is not registered yet. Parse the url to find out its + * type. If parsing fails, return failure. + */ + rv = acl_url_to_dbtype(url, dbtype); + + if (rv < 0) { + return rv; + } + + plist = PListNew(NULL); + rv = ACL_DatabaseRegister(errp, *dbtype, url, url, plist); + PListDestroy(plist); + return rv; +} + +NSAPI_PUBLIC int ACL_DatabaseFind(NSErr_t *errp, const char *name, + ACLDbType_t *dbtype, void **db) +{ + AuthdbInfo_t *info; + + *dbtype = ACL_DBTYPE_INVALID; + *db = 0; + + if (ACLDbNameHash) { + info = (AuthdbInfo_t *)PR_HashTableLookup(ACLDbNameHash, +#ifdef NSPR20 + name +#else + (char *)name +#endif + ); + + if (info) { + *dbtype = info->dbtype; + *db = info->dbinfo; + + return LAS_EVAL_TRUE; + } + } + + return LAS_EVAL_FAIL; +} + + +NSAPI_PUBLIC int ACL_ReadDbMapFile (NSErr_t *errp, const char *map_file, + int default_only) +{ + DBConfInfo_t *info; + DBConfDBInfo_t *db_info; + DBPropVal_t *propval; + PList_t plist; + int rv; + int seen_default = 0; + + if (default_only) + rv = dbconf_read_default_dbinfo(map_file, &db_info); + else + rv = dbconf_read_config_file(map_file, &info); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4600, ACL_Program, 3, XP_GetAdminStr(DBT_ReadDbMapFileErrorReadingFile), map_file, ldapu_err2string(rv)); + return -1; + } + + rv = 0; + + if (!default_only) + db_info = info->firstdb; + + while(db_info) { + char *url = db_info->url; + char *dbname = db_info->dbname; + ACLDbType_t dbtype; + + /* process db_info */ + if (url) { + rv = acl_url_to_dbtype(url, &dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4610, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileCouldntDetermineDbtype), url); + break; + } + } + else { + nserrGenerate(errp, ACLERRFAIL, ACLERR4620, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileMissingUrl), dbname); + rv = -1; + break; + } + + /* convert any property-value pairs in db_info into plist */ + plist = PListNew(NULL); + propval = db_info->firstprop; + + while(propval) { + if (propval->prop) { + PListInitProp(plist, 0, propval->prop, propval->val, 0); + } + else { + nserrGenerate(errp, ACLERRINVAL, ACLERR4630, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileInvalidPropertyPair), dbname); + rv = -1; + break; + } + propval = propval->next; + } + + if (rv < 0) break; + + /* register the database */ + rv = ACL_DatabaseRegister(errp, dbtype, dbname, url, plist); + PListDestroy(plist); + + if (rv < 0) { + /* Failed to register database */ + nserrGenerate(errp, ACLERRFAIL, ACLERR4640, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileRegisterDatabaseFailed), dbname); + break; + } + + /* If the dbname is "default", set the default_dbtype */ + if (!strcmp(dbname, DBCONF_DEFAULT_DBNAME)) { + if (!ACL_DbTypeIsEqual(errp, dbtype, ACL_DbTypeLdap)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4350, ACL_Program, 1, + XP_GetAdminStr(DBT_ReadDbMapFileDefaultDatabaseNotLdap)); + rv = -1; + break; + } + if (seen_default) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4360, ACL_Program, 1, XP_GetAdminStr(DBT_ReadDbMapFileMultipleDefaultDatabases)); + rv = -1; + break; + } + seen_default = 1; + ACL_DatabaseSetDefault(errp, dbname); + } + + db_info = db_info->next; + } + + if (!seen_default) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4370, ACL_Program, 1, XP_GetAdminStr(DBT_ReadDbMapFileMissingDefaultDatabase)); + rv = -1; + } + + if (default_only) + dbconf_free_dbinfo(db_info); + else + dbconf_free_confinfo(info); + + return rv; +} + +void +ACL_DatabaseDestroy(void) +{ + pool_destroy(ACL_DATABASE_POOL); + ACL_DATABASE_POOL = NULL; + ACLDbNameHash = NULL; + return; +} + diff --git a/lib/libaccess/avadb.c b/lib/libaccess/avadb.c new file mode 100644 index 00000000..c0d98704 --- /dev/null +++ b/lib/libaccess/avadb.c @@ -0,0 +1,300 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libaccess/ava.h" +#include "libaccess/avadb.h" +#include "base/session.h" +#include "base/pblock.h" +#include "frame/req.h" +#include "frame/log.h" + +#include "libadmin/libadmin.h" +#include "libaccess/avapfile.h" + +#define DB_NAME "AvaMap" + +enum {AVA_DB_SUCCESS=0,AVA_DB_FAILURE}; + +#ifdef XP_UNIX +#include "mcom_ndbm.h" + +USE_NSAPI int AddEntry (char *key, char *value) { + datum keyd; + datum valued; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDWR | O_CREAT, 0644); + + if (!db) + return AVA_DB_FAILURE; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + valued.dptr = value; + valued.dsize = strlen(value) + 1; + + dbm_store (db, keyd, valued, DBM_REPLACE); + dbm_close (db); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI int DeleteEntry (char *key) { + datum keyd; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDWR, 0644); + + if (!db) + return AVA_DB_FAILURE; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + dbm_delete (db, keyd); + + dbm_close (db); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI char *GetValue (char *key) { + datum keyd; + datum valued; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDONLY, 0644); + + if (!db) + return NULL; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + valued = dbm_fetch (db, keyd); + + dbm_close (db); + + return valued.dptr; +} + +#else + +#include <stdio.h> + + +#define lmemcpy memcpy +#define lmemcmp memcmp +#define lmemset memset + +static int mkhash8(char *x,int len) { + unsigned int i,hash = 0; + for (i=0; i < len; i++) { hash += x[i]; } + + return (int) (hash & 0xff); +} + +static void mkpath(char *target, char *dir, char sep, char *name) { + int len; + + len = strlen(dir); + lmemcpy(target,dir,len); + target += len; + + *target++ = sep; + + len = strlen(name); + lmemcpy(target,name,len); + target += len; + + *target = 0; +} + +#define DELETED_LEN 8 +static char DELETED[] = { 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff , 0x0 }; + + +#define RECORD_SIZE 512 +USE_NSAPI int AddEntry (char *key, char *value) { + int empty, hash; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len, val_len,size; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb+"); + if (f == NULL) { + f = fopen(dbpath,"wb+"); + } + + if (f == NULL) + return AVA_DB_FAILURE; + + key_len = strlen(key)+1; + val_len = strlen(value); + + if ((key_len+val_len) > RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + + + /* now hash the key */ + hash = mkhash8(key,key_len); + empty = -1; + + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for (;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + break; + } + if ((empty == -1) && (lmemcmp(record,DELETED,DELETED_LEN) == 0)) { + empty = hash; + } + if (record == 0) { + break; + } + hash++; + } + + if (empty != -1) { hash = empty; } + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + /* build the record */ + lmemset(record,0,RECORD_SIZE); + + lmemcpy(record,key,key_len); + lmemcpy(&record[key_len],value,val_len); + size= fwrite(record,1,RECORD_SIZE,f); + if (size != RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + fclose(f); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI int DeleteEntry (char *key) { + int found,hash; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len,size; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb+"); + + if (f == NULL) + return AVA_DB_FAILURE; + + key_len = strlen(key)+1; + + + /* now hash the key */ + hash = mkhash8(key,key_len); + found = 0; + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for (;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + found++; + break; + } + if (record == 0) { + break; + } + hash++; + } + + if (!found) { + fclose(f); + return AVA_DB_SUCCESS; + } + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + /* build the record */ + lmemset(record,0,RECORD_SIZE); + + lmemcpy(record,DELETED,DELETED_LEN); + size= fwrite(record,1,RECORD_SIZE,f); + if (size != RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + fclose(f); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI char *GetValue (char *key) { + int hash,size; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len,found = 0; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb"); + + if (f == NULL) + return NULL; + + key_len = strlen(key)+1; + + /* now hash the key */ + hash = mkhash8(key,key_len); + + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for(;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + found++; + break; + } + if (record == 0) { + break; + } + hash++; + } + + fclose(f); + if (!found) return NULL; + + return system_strdup(&record[key_len+1]); +} + +#endif diff --git a/lib/libaccess/avaparse.y b/lib/libaccess/avaparse.y new file mode 100644 index 00000000..c28ef165 --- /dev/null +++ b/lib/libaccess/avaparse.y @@ -0,0 +1,140 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +%{ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "libaccess/ava.h" +#include "libaccess/avapfile.h" +#include "netsite.h" + +extern int linenum; +extern char yytext[]; + +static void AddDefType (int defType, char *defId); +static void AddAVA (char* userID); + +void yyerror(const char* string); +extern void logerror(const char* string,int num, char *file); + +AVAEntry tempEntry; +AVATable entryTable; + +%} + +%union { + char *string; + int num; +} + +%token DEF_C DEF_CO DEF_OU DEF_CN EQ_SIGN DEF_START +%token DEF_L DEF_E DEF_ST +%token <string> USER_ID DEF_ID + +%type <num> def.type + +%start source + +%% + +source: ava.database + | + ; + + +ava.database: ava.database ava + | ava + ; + +ava: USER_ID definitions {AddAVA($1);}; + +definitions: definition.list + | + ; + +definition.list: definition.list definition + | definition + ; + + +definition: def.type EQ_SIGN DEF_ID {AddDefType($1, $3);}; + +def.type: DEF_C {$$ = DEF_C; } + | DEF_CO {$$ = DEF_CO;} + | DEF_OU {$$ = DEF_OU;} + | DEF_CN {$$ = DEF_CN;} + | DEF_L {$$ = DEF_L; } + | DEF_E {$$ = DEF_E; } + | DEF_ST {$$ = DEF_ST;} + ; + +%% + +void yyerror(const char* string) { + logerror(string,linenum,currFile); +} + + +void AddDefType (int defType, char *defId) { + switch (defType) { + case DEF_C: + tempEntry.country = defId; + break; + case DEF_CO: + tempEntry.company = defId; + break; + case DEF_OU: + if (tempEntry.numOrgs % ORGS_ALLOCSIZE == 0) { + if (tempEntry.numOrgs == 0) { + tempEntry.organizations = + PERM_MALLOC (sizeof (char*) * ORGS_ALLOCSIZE); + } else { + char **temp; + temp = + PERM_MALLOC(sizeof(char*) * (tempEntry.numOrgs + ORGS_ALLOCSIZE)); + memcpy (temp, tempEntry.organizations, + sizeof(char*)*tempEntry.numOrgs); + PERM_FREE (tempEntry.organizations); + tempEntry.organizations = temp; + } + } + tempEntry.organizations[tempEntry.numOrgs++] = defId; + break; + case DEF_CN: + tempEntry.CNEntry = defId; + break; + case DEF_E: + tempEntry.email = defId; + break; + case DEF_L: + tempEntry.locality = defId; + break; + case DEF_ST: + tempEntry.state = defId; + break; + default: + break; + } +} + +void AddAVA (char* userID) { + AVAEntry *newAVA; + + newAVA = (AVAEntry*)PERM_MALLOC(sizeof(AVAEntry)); + if (!newAVA) { + yyerror ("Out of Memory in AddAVA"); + return; + } + *newAVA = tempEntry; + newAVA->userid = userID; + + _addAVAtoTable (newAVA, &entryTable); + + tempEntry.CNEntry = tempEntry.userid = tempEntry.country = tempEntry.company = 0; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + tempEntry.numOrgs = 0; +} diff --git a/lib/libaccess/avapfile.c b/lib/libaccess/avapfile.c new file mode 100644 index 00000000..8c955d9d --- /dev/null +++ b/lib/libaccess/avapfile.c @@ -0,0 +1,428 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libaccess/ava.h" + +#include "base/session.h" +#include "base/pblock.h" +#include "frame/req.h" +#include "frame/log.h" + +#include "libadmin/libadmin.h" +#include "libaccess/avapfile.h" + +#define ALLOC_SIZE 20 +#define SUCCESS 0 + +struct parsedStruct { + char *fileName; + AVATable *avaTable; +}; + +typedef struct parsedStruct Parsed; + +/* globals for yy_error if needed */ +Session *yy_sn = NULL; +Request *yy_rq = NULL; + +/*This will be a dynamic array of parsedStruct*. Re-sizing if necessary.*/ +struct ParsedTable { + Parsed **parsedTable; + int numEntries; +}; + +char *currFile; + +static struct ParsedTable parsedFiles = {NULL, 0}; + +extern AVATable entryTable; /*Table where entries are stored*/ +extern AVAEntry tempEntry; /*Used to restore parser's state*/ +extern linenum; + +AVAEntry * AVAEntry_Dup(AVAEntry *entry) { + int i; + AVAEntry *newAVA = NULL; +/* copy the AVA entry */ + + if (entry) { + newAVA = (AVAEntry *) PERM_MALLOC(sizeof(AVAEntry)); + memset(newAVA,0, sizeof(AVAEntry)); + newAVA->userid = 0; + newAVA->CNEntry = 0; + newAVA->email = 0; + newAVA->locality = 0; + newAVA->state = 0; + newAVA->country = 0; + newAVA->company = 0; + newAVA->organizations = 0; + newAVA->numOrgs = 0; + if (entry->userid) newAVA->userid = PERM_STRDUP(entry->userid); + if (entry->CNEntry) newAVA->CNEntry = PERM_STRDUP(entry->CNEntry); + if (entry->email) newAVA->email = PERM_STRDUP(entry->email); + if (entry->locality) newAVA->locality = PERM_STRDUP(entry->locality); + if (entry->state) newAVA->state = PERM_STRDUP(entry->state); + if (entry->country) newAVA->country = PERM_STRDUP(entry->country); + if (entry->company) newAVA->company = PERM_STRDUP(entry->company); + if (entry->organizations) { + newAVA->organizations = PERM_MALLOC(sizeof(char *)*entry->numOrgs); + newAVA->numOrgs = entry->numOrgs; + for (i=0; i<entry->numOrgs; i++) + newAVA->organizations[i] = PERM_STRDUP (entry->organizations[i]); + } + } + return newAVA; +} + +void _addAVAtoTable (AVAEntry *newAVA, AVATable *table) { + int i; + int insertIndex = -1; + + if (table->numEntries%ENTRIES_ALLOCSIZE == 0) { + if (table->numEntries == 0) { + table->enteredTable = + (AVAEntry**) PERM_MALLOC (sizeof(AVAEntry*) * ENTRIES_ALLOCSIZE); + } else { + AVAEntry **temp; + + temp = + PERM_MALLOC(sizeof(AVAEntry*)*(table->numEntries+ENTRIES_ALLOCSIZE)); + memmove(temp, table->enteredTable, sizeof(AVAEntry*)*table->numEntries); + PERM_FREE(table->enteredTable); + table->enteredTable = temp; + } + } + + for (i=table->numEntries-1; i >= 0; i--) { + if (strcmp(newAVA->userid, table->enteredTable[i]->userid) > 0) { + insertIndex = i+1; + break; + } else { + table->enteredTable[i+1] = table->enteredTable[i]; + } + } + + + table->enteredTable[(insertIndex == -1) ? 0 : insertIndex] = newAVA; + (table->numEntries)++; +} + +AVATable *AVATableDup(AVATable *table) { + AVATable *newTable = (AVATable*)PERM_MALLOC (sizeof(AVATable)); + /* round the puppy so _addAVAtoTable still works */ + int size = (table->numEntries + (ENTRIES_ALLOCSIZE-1))/ENTRIES_ALLOCSIZE; + int i; + + newTable->enteredTable = + (AVAEntry**)PERM_MALLOC(size*ENTRIES_ALLOCSIZE*sizeof(AVAEntry *)); + + for (i=0; i < table->numEntries; i++) { + newTable->enteredTable[i] = AVAEntry_Dup(table->enteredTable[i]); + } + newTable->numEntries = table->numEntries; + return newTable; +} + + + + +AVAEntry *_getAVAEntry(char *groupName, AVATable *mapTable) { + char line[BIG_LINE]; + int lh, rh, mid, cmp;; + + if (!mapTable) { + sprintf (line, "NULL Pointer passed as mapTable when trying to get entry %s", groupName); + report_error (SYSTEM_ERROR, "File Not Found", line); + } + + + lh = 0; + rh = mapTable->numEntries-1; + + while (lh <= rh) { + mid = lh + ((rh-lh)/2); + cmp = strcmp(groupName, mapTable->enteredTable[mid]->userid); + if (cmp == 0) + return mapTable->enteredTable[mid]; + else if (cmp > 0) + lh = mid + 1; + else + rh = mid - 1; + } + + return NULL; + +} + +AVATable *_getTable (char *fileName) { + int lh, rh, mid, cmp; + AVATable *table = NULL; + + /*First checks to see if it's already been parsed*/ + + lh = 0; + rh = parsedFiles.numEntries-1; + while (lh <= rh) { + mid = lh + ((rh - lh)/2); + cmp = strcmp(fileName, parsedFiles.parsedTable[mid]->fileName); + if (cmp == SUCCESS) { + return parsedFiles.parsedTable[mid]->avaTable; + } else if (cmp < SUCCESS) { + rh = mid-1; + } else { + lh = mid+1; + } + } + + yyin = fopen (fileName, "r"); + + if (yyin) { + if (!yyparse()) { + table = _wasParsed (fileName); + table->userdb = NULL; + } + fclose (yyin); + } + + return table; +} + +int _hasBeenParsed (char *aclFileName){ + return (_getTable(aclFileName) != NULL); +} + +AVATable* _wasParsed (char *inFileName) { + Parsed *newEntry; + int i; + + if (!inFileName) + return NULL; + + newEntry = (Parsed*) PERM_MALLOC (sizeof(Parsed)); + newEntry->fileName = PERM_STRDUP (inFileName); + newEntry->avaTable = AVATableDup(&entryTable); + + if (parsedFiles.numEntries % ALLOC_SIZE == 0) { + if (parsedFiles.numEntries) { + Parsed **temp; + + temp = PERM_MALLOC (sizeof(Parsed*)*(parsedFiles.numEntries + ALLOC_SIZE)); + if (!temp) + return NULL; + memcpy (temp, parsedFiles.parsedTable, sizeof(Parsed*)*parsedFiles.numEntries); + PERM_FREE (parsedFiles.parsedTable); + parsedFiles.parsedTable = temp; + } else { + parsedFiles.parsedTable = + (Parsed**) PERM_MALLOC (sizeof (Parsed*) * ALLOC_SIZE); + if (!parsedFiles.parsedTable) + return NULL; + } + } + for (i=parsedFiles.numEntries; i > 0; i--) { + if (strcmp(newEntry->fileName,parsedFiles.parsedTable[i-1]->fileName) < 0) { + parsedFiles.parsedTable[i] = parsedFiles.parsedTable[i-1]; + } else { + break; + } + } + parsedFiles.parsedTable[i] = newEntry; + parsedFiles.numEntries++; + +/*Initialize parser structures to resemble that before parse*/ + entryTable.numEntries = 0; + tempEntry.country = tempEntry.company = tempEntry.CNEntry = NULL; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + linenum = 1; + + return newEntry->avaTable; +} + +AVAEntry *_deleteAVAEntry (char *group, AVATable *table) { + int removeIndex; + int lh, rh, mid, cmp; + AVAEntry *entry = NULL; + + if (!group || !table) + return NULL; + + lh = 0; + rh = table->numEntries - 1; + + while (lh <= rh) { + mid = lh + ((rh-lh)/2); + cmp = strcmp (group, table->enteredTable[mid]->userid); + if (cmp == SUCCESS) { + removeIndex = mid; + break; + } else if (cmp < SUCCESS) { + rh = mid-1; + } else { + lh = mid+1; + } + } + + if (lh > rh) + return NULL; + + entry = table->enteredTable[removeIndex]; + + memmove ((char*)(table->enteredTable)+(sizeof(AVAEntry*)*removeIndex), + (char*)(table->enteredTable)+(sizeof(AVAEntry*)*(removeIndex+1)), + (table->numEntries - removeIndex - 1)*sizeof(AVAEntry*)); + + (table->numEntries)--; + + return entry; +} + +void AVAEntry_Free (AVAEntry *entry) { + int i; + + if (entry) { + if (entry->userid) + PERM_FREE (entry->userid); + if (entry->CNEntry) + PERM_FREE (entry->CNEntry); + if (entry->email) + PERM_FREE (entry->email); + if (entry->locality) + PERM_FREE (entry->locality); + if (entry->state) + PERM_FREE (entry->state); + if (entry->country) + PERM_FREE (entry->country); + if (entry->company) + PERM_FREE (entry->company); + if (entry->organizations) { + for (i=0; i<entry->numOrgs; i++) + PERM_FREE (entry->organizations[i]); + PERM_FREE(entry->organizations); + } + } +} + +void PrintHeader(FILE *outfile){ + + fprintf (outfile,"/*This file is generated automatically by the admin server\n"); + fprintf (outfile," *Any changes you make manually may be lost if other\n"); + fprintf (outfile," *changes are made through the admin server.\n"); + fprintf (outfile," */\n\n\n"); + +} + +void writeOutEntry (FILE *outfile, AVAEntry *entry) { + int i; + + /*What should I do if the group id is not there?*/ + if (!entry || !(entry->userid)) + report_error (SYSTEM_ERROR, "AVA-DB Failure", + "Bad entry passed to write out function"); + + fprintf (outfile,"%s: {\n", entry->userid); + if (entry->CNEntry) + fprintf (outfile,"\tCN=\"%s\"\n", entry->CNEntry); + if (entry->email) + fprintf (outfile,"\tE=\"%s\"\n", entry->email); + if (entry->company) + fprintf (outfile,"\tO=\"%s\"\n", entry->company); + if (entry->organizations) { + for (i=0; i < entry->numOrgs; i++) { + fprintf (outfile, "\tOU=\"%s\"\n", entry->organizations[i]); + } + } + if (entry->locality) + fprintf (outfile,"\tL=\"%s\"\n",entry->locality); + if (entry->state) + fprintf (outfile,"\tST=\"%s\"\n",entry->state); + if (entry->country) + fprintf (outfile,"\tC=\"%s\"\n", entry->country); + + fprintf (outfile,"}\n\n\n"); + +} + +void writeOutFile (char *authdb, AVATable *table) { + char line[BIG_LINE]; + char mess[200]; + FILE *newfile; + int i; + + sprintf (line, "%s%c%s%c%s.%s", get_authdb_dir(), FILE_PATHSEP, authdb, FILE_PATHSEP, + AUTH_DB_FILE, AVADB_TAG); + + if (!table) { + sprintf (mess, "The structure for file %s was not loaded before writing out", line); + report_error (SYSTEM_ERROR, "Internal Error", mess); + } + + newfile = fopen (line, "w"); + + if (!newfile) { + sprintf (mess, "Could not open file %s for writing.", line); + report_error(FILE_ERROR, "No File", mess); + } + + PrintHeader (newfile); + + for (i=0;i < table->numEntries; i++) { + writeOutEntry (newfile, table->enteredTable[i]); + } + + fclose(newfile); +} + + +void +logerror(char *error,int line,char *file) { + /* paranoia */ + /*ava-mapping is only functin that initializes yy_sn and yy_rq*/ + if ((yy_sn != NULL) && (yy_rq != NULL)) { + log_error (LOG_FAILURE, "ava-mapping", yy_sn, yy_rq, + "Parse error line %d of %s: %s", line, file, error); + } else { + char errMess[250]; + + sprintf (errMess, "Parse error line %d of %s: %s", line, file, error); + report_error (SYSTEM_ERROR, "Failure: Loading AVA-DB Table", errMess); + } +} + + +void outputAVAdbs(char *chosen) { + char *authdbdir = get_authdb_dir(); + char **listings; + int i; + int numListings = 0; + int hasOptions = 0; + + listings = list_auth_dbs(authdbdir); + + while (listings[numListings++] != NULL); + + for (i=0; listings[i] != NULL ; i++) { + if (!hasOptions) { + printf ("<select name=\"%s\"%s onChange=\"form.submit()\">",AVA_DB_SEL, + (numListings > SELECT_OVERFLOW)?"size=5":""); + hasOptions = 1; + } + + printf ("<option value=\"%s\"%s>%s\n",listings[i], + (strcmp(chosen, listings[i]) == 0) ? "SELECTED":"",listings[i]); + } + + if (hasOptions) + printf ("</select>\n"); + else + printf ("<i><b>Insert an AVA-Database entry first</b></i>\n");/*This should never happen, + *since I never create an empty + *avadb file, + *but one never knows + */ + +} diff --git a/lib/libaccess/avascan.l b/lib/libaccess/avascan.l new file mode 100644 index 00000000..ceae1426 --- /dev/null +++ b/lib/libaccess/avascan.l @@ -0,0 +1,106 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +%{ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "y.tab.h" +#include "libaccess/ava.h" +#include "netsite.h" + +int linenum = 1; +int first_time = 1; +int old_state; +int num_nested_comments = 0; + +extern AVAEntry tempEntry; +extern AVATable entryTable; + +void strip_quotes(void); + +%} + +%s COMMENT NORM DEFINES DEF_TYPE + +uc_letter [A-Z] +lc_letter [a-z] +digit [0-9] +under_score _ + +letter ([A-Z,a-z]) + +white_space ([ \t]*) +identifier ([_,A-Z,a-z][_,A-Z,a-z,0-9]*) +def_identifier (({white_space}{identifier})+) +text (\"[^\"]*\") +comments (([^"*/"\n])*) + + + +%% + +%{ + if (first_time) { + BEGIN NORM; + first_time = tempEntry.numOrgs = 0; + old_state = NORM; + tempEntry.userid = 0; + tempEntry.country = 0; + tempEntry.CNEntry = 0; + tempEntry.email = 0; + tempEntry.locality = 0; + tempEntry.state = 0; + entryTable.numEntries = 0; + } +%} + + +"/*" {BEGIN COMMENT; num_nested_comments++;} +<COMMENT>"*/" {num_nested_comments--; + if (!num_nested_comments) BEGIN old_state;} +<COMMENT>. {;} + +<NORM>{identifier} {yylval.string = PERM_STRDUP(yytext); + return USER_ID;} +<NORM>":"{white_space}\{ {BEGIN DEF_TYPE; + old_state = DEF_TYPE;} + +<DEF_TYPE>"C" {BEGIN DEFINES; old_state = DEFINES; + return DEF_C; } +<DEF_TYPE>"O" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CO;} +<DEF_TYPE>"OU" {BEGIN DEFINES; old_state = DEFINES; + return DEF_OU;} +<DEF_TYPE>"CN" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CN;} +<DEF_TYPE>"L" {BEGIN DEFINES; old_state = DEFINES; + return DEF_L;} +<DEF_TYPE>"E" {BEGIN DEFINES; old_state = DEFINES; + return DEF_E;} +<DEF_TYPE>"ST" {BEGIN DEFINES; old_state = DEFINES; + return DEF_ST;} +<DEF_TYPE>"}" {BEGIN NORM;old_state = NORM;} + +<DEFINES>= {return EQ_SIGN;} +<DEFINES>{text} {BEGIN DEF_TYPE; old_state = DEF_TYPE; + strip_quotes(); + return DEF_ID;} + +{white_space} {;} +\n {linenum++;} +. {yyerror("Bad input character");} +%% + +int yywrap () { + return 1; +} + +void strip_quotes(void) { + yytext[strlen(yytext)-1]= '\0'; + yylval.string = PERM_STRDUP(&yytext[1]); +} diff --git a/lib/libaccess/las.h b/lib/libaccess/las.h new file mode 100644 index 00000000..3dfdf1e9 --- /dev/null +++ b/lib/libaccess/las.h @@ -0,0 +1,53 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LAS_H +#define LAS_H + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include <base/plist.h> + +NSPR_BEGIN_EXTERN_C + +extern int LASTimeOfDayEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASDayOfWeekEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASIpEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASDnsEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASGroupEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASUserEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +/* MLM - for admin delegation */ +extern int LASProgramEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); + +extern void LASTimeOfDayFlush(void **cookie); +extern void LASDayOfWeekFlush(void **cookie); +extern void LASIpFlush(void **cookie); +extern void LASDnsFlush(void **cookie); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/lasdns.cpp b/lib/libaccess/lasdns.cpp new file mode 100644 index 00000000..22a37dac --- /dev/null +++ b/lib/libaccess/lasdns.cpp @@ -0,0 +1,373 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasdns.c + * This file contains the DNS LAS code. + */ + +#include <stdio.h> +#include <string.h> + +#ifdef XP_WIN32 +/* #include <winsock2.h> */ +#include <winsock.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#include <netsite.h> +extern "C" { +#include <prnetdb.h> +} +#include <base/plist.h> +#include <base/pool.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 "lasdns.h" +#include "aclutil.h" +#include "aclcache.h" +#include "permhash.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "access_plhash.h" +extern "C" { +#include <nspr.h> +} + +#ifdef UTEST +extern int LASDnsGetDns(char **dnsv); +#endif + +/* LASDnsMatch + * Given an array of fully-qualified dns names, tries to match them + * against a given hash table. + * INPUT + * dns DNS string + * context pointer to an LAS DNS context structure + * + * In our usage, this context is derived from + * an acllist cache, which is a representation of + * of some global acis--in other words it's a global thing + * shared between threads--hence the use + * of the "read-only" hash lookup routines here. + */ +int +LASDnsMatch(char *token, LASDnsContext_t *context) +{ + + /* Test for the unusual case where "*" is allowed */ + if (ACL_HashTableLookup_const(context->Table, "*")) + return LAS_EVAL_TRUE; + + /* Start with the full name. Then strip off each component + * leaving the remainder starting with a period. E.g. + * splash.mcom.com + * .mcom.com + * .com + * Search the hash table for each remaining portion. Remember that + * wildcards were put in with the leading period intact. + */ + do { + if (ACL_HashTableLookup_const(context->Table, token)) + return LAS_EVAL_TRUE; + + token = strchr(&token[1], '.'); + } while (token != NULL); + + return LAS_EVAL_FALSE; + +} + +/* LASDNSBuild + * Builds a hash table of all the hostnames provided (plus their aliases + * if aliasflg is true). Wildcards are only permitted in the leftmost + * field. They're represented in the hash table by a leading period. + * E.g. ".mcom.com". + * + * RETURNS Zero on success, else LAS_EVAL_INVALID + */ +int +LASDnsBuild(NSErr_t *errp, char *attr_pattern, LASDnsContext_t *context, int aliasflg) +{ + int delimiter; /* length of valid token */ + char token[256]; /* max length dns name */ + int i; + int ipcnt; + char **p; + unsigned long *ipaddrs=0; + pool_handle_t *pool; + PRStatus error=PR_SUCCESS; + char buffer[PR_NETDB_BUF_SIZE]; +#ifdef UTEST + struct hostent *he, host; +#else + PRHostEnt *he, host; +#endif + + context->Table = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + pool = pool_create(); + context->pool = pool; + if ((!context->Table) || (!context->pool)) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4700, ACL_Program, 1, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAllocateHashT_)); + return LAS_EVAL_INVALID; + } + + do { + /* Get a single hostname from the pattern string */ + delimiter = strcspn(attr_pattern, ", \t"); + strncpy(token, attr_pattern, delimiter); + token[delimiter] = '\0'; + + /* Skip any white space after the token */ + attr_pattern += delimiter; + attr_pattern += strspn(attr_pattern, ", \t"); + + /* If there's a wildcard, strip it off but leave the "." + * Can't have aliases for a wildcard pattern. + * Treat "*" as a special case. If so, go ahead and hash it. + */ + if (token[0] == '*') { + if (token[1] != '\0') { + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[1]), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4710, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + } else { + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, token), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4720, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + } + } else { + /* This is a single hostname add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[0]), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4730, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + + if (aliasflg) { + /* memset(buffer, '\0', PR_NETDB_BUF_SIZE); + */ +#ifdef UTEST + he = gethostbyname(token); +#else + error = PR_GetHostByName(token, + buffer, + PR_NETDB_BUF_SIZE, + &host); + if (error == PR_SUCCESS) he = &host; + else he = NULL; +#endif + if (he) { + /* Make a copy of the list of IP addresses if any */ + if (he->h_addr_list && he->h_addr_list[0]) { + + /* Count the IP addresses */ + for (p = he->h_addr_list, ipcnt = 0; *p; ++p) { + ++ipcnt; + } + + /* Allocate space */ + ipaddrs = (unsigned long *)PERM_MALLOC(ipcnt * sizeof(unsigned long)); + + /* Copy IP addresses */ + for (i = 0; i < ipcnt; ++i) { + ipaddrs[i] = 0; + memcpy((void *)&ipaddrs[i], he->h_addr_list[i], 4); + } + } + + /* Add each of the aliases to the list */ + if (he->h_aliases && he->h_aliases[0]) { + + for (p = he->h_aliases; *p; ++p) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void*)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4740, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + } + + for (i = 0; i < ipcnt; ++i) { + +#ifdef UTEST + he = gethostbyaddr((char *)&ipaddrs[i], 4, AF_INET); +#else + error = PR_GetHostByAddr((PRNetAddr *)&ipaddrs[i], + buffer, + PR_NETDB_BUF_SIZE, + &host); + if (error == PR_SUCCESS) he = &host; + else he = NULL; +#endif + if (he) { + if (he->h_name) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, he->h_name), + (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4750, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), he->h_name); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + + if (he->h_aliases && he->h_aliases[0]) { + for (p = he->h_aliases; *p; ++p) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4760, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + } + } + } + + PERM_FREE(ipaddrs); + + } /* if he */ + + + } /* if aliasflg */ + + } /* else - single hostname */ + + + } while ((attr_pattern != NULL) && + (attr_pattern[0] != '\0') && + (delimiter != (int)NULL)); + + return 0; +} + +/* LASDnsFlush + * Given the address of a las_cookie for a DNS expression entry, frees up + * all allocated memory for it. This includes the hash table, plus the + * context structure. + */ +void +LASDnsFlush(void **las_cookie) +{ + if (*las_cookie == NULL) + return; + + pool_destroy(((LASDnsContext_t *)*las_cookie)->pool); + PR_HashTableDestroy(((LASDnsContext_t *)*las_cookie)->Table); + PERM_FREE(*las_cookie); + *las_cookie = NULL; + return; +} + +/* + * LASDnsEval + * INPUT + * attr_name The string "dns" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of DNS names + * Any segment(s) in a DNS name can be wildcarded using + * "*". Note that this is not a true Regular Expression + * form. + * *cachable Always set to ACL_INDEF_CACHE + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * ret code The usual LAS return codes. + */ +int LASDnsEval(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 result; + int aliasflg; + char *my_dns; + LASDnsContext_t *context; + int rv; + + *cachable = ACL_INDEF_CACHABLE; + + if (strcmp(attr_name, "dns") == 0) + aliasflg = 0; + else if (strcmp(attr_name, "dnsalias") == 0) + aliasflg = 1; + else { + nserrGenerate(errp, ACLERRINVAL, ACLERR4800, ACL_Program, 2, XP_GetAdminStr(DBT_lasDnsBuildReceivedRequestForAtt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4810, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + /* If this is the first time through, build the pattern tree first. */ + if (*LAS_cookie == NULL) { + ACL_CritEnter(); + if (*LAS_cookie == NULL) { /* Must check again */ + *LAS_cookie = context = + (LASDnsContext_t *)PERM_MALLOC(sizeof(LASDnsContext_t)); + if (context == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4820, ACL_Program, 1, XP_GetAdminStr(DBT_lasdnsevalUnableToAllocateContex_)); + ACL_CritExit(); + return LAS_EVAL_FAIL; + } + context->Table = NULL; + LASDnsBuild(errp, attr_pattern, context, aliasflg); + } + ACL_CritExit(); + } else + context = (LASDnsContext *) *LAS_cookie; + + /* Call the DNS attribute getter */ +#ifdef UTEST + LASDnsGetDns(&my_dns); /* gets stuffed on return */ +#else + rv = ACL_GetAttribute(errp, ACL_ATTR_DNS, (void **)&my_dns, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + if (subject || resource) { + char rv_str[16]; + /* Don't ereport if called from ACL_CachableAclList */ + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR4830, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalUnableToGetDnsErrorDN_), rv_str); + } + return LAS_EVAL_FAIL; + } +#endif + + result = LASDnsMatch(my_dns, context); + + if (comparator == CMP_OP_NE) { + if (result == LAS_EVAL_FALSE) + return LAS_EVAL_TRUE; + else if (result == LAS_EVAL_TRUE) + return LAS_EVAL_FALSE; + } + return (result); +} diff --git a/lib/libaccess/lasdns.h b/lib/libaccess/lasdns.h new file mode 100644 index 00000000..3d86dc98 --- /dev/null +++ b/lib/libaccess/lasdns.h @@ -0,0 +1,10 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef struct LASDnsContext { + PRHashTable *Table; + pool_handle_t *pool; +} LASDnsContext_t; diff --git a/lib/libaccess/lasgroup.cpp b/lib/libaccess/lasgroup.cpp new file mode 100644 index 00000000..6ff5d1bb --- /dev/null +++ b/lib/libaccess/lasgroup.cpp @@ -0,0 +1,164 @@ +/** 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 */ + +/* lasgroup.c + * This file contains the Group LAS code. + */ +#include <stdio.h> +#include <string.h> +#include <netsite.h> +#include "aclpriv.h" +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include <ldaputil/errors.h> /* for DBG_PRINT */ +#include "aclutil.h" + +#ifdef UTEST +extern char *LASGroupGetUser(); +#endif /* UTEST */ + + +/* + * LASGroupEval + * INPUT + * attr_name The string "group" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of groups + * *cachable Always set to ACL_NOT_CACHABLE + * subject Subjust property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASGroupEval(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) +{ + char *groups = attr_pattern; + int retcode; + char *member_of; + char *user; + char *dbname; + time_t *req_time = 0; + const char *group; + char delim; + int len; + int rv; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_GROUP) != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4900, ACL_Program, 2, XP_GetAdminStr(DBT_lasGroupEvalReceivedRequestForAt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4910, ACL_Program, 2, XP_GetAdminStr(DBT_lasgroupevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* Get the authenticated user */ + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&user, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR4920, ACL_Program, 2, XP_GetAdminStr(DBT_lasGroupEvalUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + rv = LAS_EVAL_FALSE; + + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + /* Loop through all the groups and check if any is in the cache */ + group = groups; + delim = ','; + while((group = acl_next_token_len(group, delim, &len)) != NULL) { + rv = acl_usr_cache_group_len_check(user, dbname, group, len, *req_time); + if (rv == LAS_EVAL_TRUE) { + /* cached group exists */ + break; + } + if (0 != (group = strchr(group+len, delim))) + group++; + else + break; + } + /* group need not be NULL-terminated */ + /* If you need to use it, copy it properly */ + group = 0; + } + + if (rv != LAS_EVAL_TRUE) { + /* not found in the cache or not one of the groups we want */ + PListDeleteProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS); + PListInitProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS, groups, 0); + PListDeleteProp(subject, ACL_ATTR_USER_ISMEMBER_INDEX, ACL_ATTR_USER_ISMEMBER); + rv = ACL_GetAttribute(errp, ACL_ATTR_USER_ISMEMBER, (void **)&member_of, + subject, resource, auth_info, global_auth); + + PListDeleteProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS); + + if (rv != LAS_EVAL_TRUE && rv != LAS_EVAL_FALSE) { + return rv; + } + + if (rv == LAS_EVAL_TRUE) { + /* User is a member of one of the groups */ + /* update the user's cache */ + acl_usr_cache_set_group(user, dbname, member_of, *req_time); + } + } + + if (rv == LAS_EVAL_TRUE) { + retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + /* User is not a member of any of the groups */ + retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + DBG_PRINT4("%s LASGroupEval: uid = \"%s\" groups = \"%s\"\n", + (retcode == LAS_EVAL_FALSE) ? "LAS_EVAL_FALSE" + : (retcode == LAS_EVAL_TRUE) ? "LAS_EVAL_TRUE" + : "Error", + user, attr_pattern); + + return retcode; +} + + +/* LASGroupFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASGroupFlush(void **las_cookie) +{ + /* do nothing */ + return; +} 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; +} + diff --git a/lib/libaccess/lasip.h b/lib/libaccess/lasip.h new file mode 100644 index 00000000..0e162d9b --- /dev/null +++ b/lib/libaccess/lasip.h @@ -0,0 +1,13 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef struct LASIpTree { + struct LASIpTree *action[2]; +} LASIpTree_t; + +typedef struct LASIpContext { + LASIpTree_t *treetop; /* Top of the pattern tree */ +} LASIpContext_t; diff --git a/lib/libaccess/lastod.cpp b/lib/libaccess/lastod.cpp new file mode 100644 index 00000000..d304986d --- /dev/null +++ b/lib/libaccess/lastod.cpp @@ -0,0 +1,170 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* Source file for the TimeOfDay and DayOfWeek LAS drivers +*/ +#include <time.h> + +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "aclutil.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +/* Day of the week LAS driver + * Note that everything is case-insensitive. + * INPUT + * attr must be the string "dayofweek". + * comparator can only be "=" or "!=". + * pattern any sequence of 3-letter day names. I.e. sun, mon, + * tue, wed, thu, fri, sat. Comma delimiters can be used + * but are not necessary. E.g. mon,TueweDThuFRISat + * OUTPUT + * cachable Will be set to ACL_NOT_CACHABLE. + * return code set to LAS_EVAL_* + */ +int +LASDayOfWeekEval(NSErr_t *errp, char *attr, CmpOp_t comparator, char *pattern, + ACLCachable_t *cachable, void **las_cookie, PList_t subject, + PList_t resource, PList_t auth_info, PList_t global_auth) +{ +#ifndef UTEST + struct tm *tm_p, tm; +#endif + time_t t; + char daystr[5]; /* Current local day in ddd */ + char lcl_pattern[512]; + char *compare; + + /* Sanity checking */ + if (strcmp(attr, "dayofweek") != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5400, ACL_Program, 2, XP_GetAdminStr(DBT_unexpectedAttributeInDayofweekSN_), attr); + return LAS_EVAL_INVALID; + } + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5410, ACL_Program, 2, XP_GetAdminStr(DBT_illegalComparatorForDayofweekDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + *cachable = ACL_NOT_CACHABLE; + + /* Obtain and format the local time */ +#ifndef UTEST + t = time(NULL); + tm_p = system_localtime(&t, &tm); + util_strftime(daystr, "%a", tm_p); +#else + t = (0x1000000); /* Mon 2120 hr */ + strftime(daystr, 4, "%a", localtime(&t)); +#endif + makelower(daystr); + strcpy(lcl_pattern, pattern); + makelower(lcl_pattern); + + /* Compare the value to the pattern */ + compare = strstr(lcl_pattern, daystr); + + if ((compare != NULL) && (comparator == CMP_OP_EQ)) + return LAS_EVAL_TRUE; + if ((compare == NULL) && (comparator == CMP_OP_NE)) + return LAS_EVAL_TRUE; + return LAS_EVAL_FALSE; +} + + +/* Time of day LAS + * INPUT + * attr must be "timeofday". + * comparator one of =, !=, >, <, >=, <= + * pattern HHMM military 24-hour clock. E.g. 0700, 2315. + * OUTPUT + * cachable will be set to ACL_NOT_CACHABLE. + * return code set to LAS_EVAL_* + */ +int +LASTimeOfDayEval(NSErr_t *errp, char *attr, CmpOp_t comparator, char *pattern, + ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, + PList_t resource, PList_t auth_info, PList_t global_auth) +{ +#ifndef UTEST + struct tm *tm_p, tm; +#endif + time_t t; + char timestr[6]; /* Current local time in HHMM */ + char start[6], end[6]; + int compare; /* >0, 0, <0 means that current time is greater, equal to, or less than the pattern */ + char *dash; + int intpattern, inttime, intstart, intend; + + if (strcmp(attr, "timeofday") != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5600, ACL_Program, 2, XP_GetAdminStr(DBT_unexpectedAttributeInTimeofdaySN_), attr); + return LAS_EVAL_INVALID; + } + *cachable = ACL_NOT_CACHABLE; + + /* Obtain and format the local time */ +#ifndef UTEST + t = time(NULL); + tm_p = system_localtime(&t, &tm); + util_strftime(timestr, "%H%M", tm_p); +#else + t = (0x1000000); /* Mon 2120 hr */ + strftime(timestr, 5, "%H%M", localtime(&t)); +#endif +#ifdef DEBUG + printf ("timestr = %s\n", timestr); +#endif + inttime = atoi(timestr); + + + dash = strchr(pattern, '-'); + if (dash) { + if (comparator != CMP_OP_EQ && comparator != CMP_OP_NE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5610, ACL_Program, 2, XP_GetAdminStr(DBT_illegalComparatorForTimeOfDayDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + strncpy(start, pattern, dash-pattern); + start[dash-pattern]='\0'; + intstart = atoi(start); + + strcpy(end, dash+1); + intend = atoi(end); + + if (intend >= intstart) { + return(evalComparator(comparator, !(inttime >= intstart && inttime <= intend))); + } else { /* range wraps around midnight */ + return(evalComparator(comparator, !(inttime >= intstart || inttime <= intend))); + } + } + + + /* ELSE - Just a single time value given. */ + + /* Compare the value to the pattern */ + intpattern = atoi(pattern); + compare = inttime - intpattern; + + /* Test against what the user wanted done */ + return(evalComparator(comparator, compare)); +} + +void +LASDayOfWeekFlush(void **cookie) +{ + return; +} + +void +LASTimeOfDayFlush(void **cookie) +{ + return; +} + diff --git a/lib/libaccess/lasuser.cpp b/lib/libaccess/lasuser.cpp new file mode 100644 index 00000000..d58f9a3f --- /dev/null +++ b/lib/libaccess/lasuser.cpp @@ -0,0 +1,154 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasuser.c + * This file contains the User LAS code. + */ + +#include <netsite.h> +#include <base/shexp.h> +#include <base/util.h> +#include <libaccess/las.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "aclutil.h" + +#ifdef UTEST +extern char * LASUserGetUser(); +#endif + + +/* + * LASUserEval + * INPUT + * attr_name The string "user" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of users + * *cachable Always set to ACL_NOT_CACHABLE. + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASUserEval(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) +{ + char *uid; + char *users; + char *user; + char *comma; + int retcode; + int matched; + int is_owner; + int rv; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_USER) != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5700, ACL_Program, 2, XP_GetAdminStr(DBT_lasUserEvalReceivedRequestForAtt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5710, ACL_Program, 2, XP_GetAdminStr(DBT_lasuserevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* get the authenticated user name */ +#ifndef UTEST + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } +#else + uid = (char *)LASUserGetUser(); +#endif + + /* We have an authenticated user */ + if (!strcmp(attr_pattern, "all")) { + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + users = STRDUP(attr_pattern); + + if (!users) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR5720, ACL_Program, 1, + XP_GetAdminStr(DBT_lasuserevalRanOutOfMemoryN_)); + return LAS_EVAL_FAIL; + } + + user = users; + matched = 0; + + /* check if the uid is one of the users */ + while(user != 0 && *user != 0 && !matched) { + if ((comma = strchr(user, ',')) != NULL) { + *comma++ = 0; + } + + /* ignore leading whitespace */ + while(*user == ' ' || *user == '\t') user++; + + if (*user) { + /* ignore trailing whitespace */ + int len = strlen(user); + char *ptr = user+len-1; + + while(*ptr == ' ' || *ptr == '\t') *ptr-- = 0; + } + + if (!strcasecmp(user, ACL_ATTR_OWNER)) { + rv = ACL_GetAttribute(errp, ACL_ATTR_IS_OWNER, (void **)&is_owner, + subject, resource, auth_info, global_auth); + if (rv == LAS_EVAL_TRUE) + matched = 1; + else + /* continue checking for next user */ + user = comma; + } + else if (!WILDPAT_CASECMP(uid, user)) { + /* uid is one of the users */ + matched = 1; + } + else { + /* continue checking for next user */ + user = comma; + } + } + + if (comparator == CMP_OP_EQ) { + retcode = (matched ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + retcode = (matched ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + FREE(users); + return retcode; +} + + +/* LASUserFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASUserFlush(void **las_cookie) +{ + /* do nothing */ + return; +} diff --git a/lib/libaccess/lcache.h b/lib/libaccess/lcache.h new file mode 100644 index 00000000..0e39a3a6 --- /dev/null +++ b/lib/libaccess/lcache.h @@ -0,0 +1,23 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef CACHE_H +#define CACHE_H + +NSPR_BEGIN_EXTERN_C + +extern void ACL_ListHashUpdate(ACLListHandle_t **acllistp); +extern void ACL_Init(void); +extern void ACL_CritEnter(void); +extern void ACL_CritExit(void); +extern ENTRY *ACL_GetUriHash(ENTRY item, ACTION action); +extern int ACL_CacheCheck(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheEnter(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheAbort(ACLListHandle_t **acllist_p); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/ldapacl.cpp b/lib/libaccess/ldapacl.cpp new file mode 100644 index 00000000..ad9d7f7f --- /dev/null +++ b/lib/libaccess/ldapacl.cpp @@ -0,0 +1,822 @@ +/** 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> +#include <base/rwlock.h> +#include <base/ereport.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "aclutil.h" +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> +#include <ldaputil/ldaputil.h> +#include <ldaputil/dbconf.h> +#include <ldaputil/ldapauth.h> +#include <libaccess/authdb.h> +#include <libaccess/ldapacl.h> +#include <libaccess/usrcache.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclglobal.h> +#include <libaccess/aclerror.h> + +#define BIG_LINE 1024 + +static int need_ldap_over_ssl = 0; +static RWLOCK ldb_rwlock = (RWLOCK)0; + +void init_ldb_rwlock () +{ + ldb_rwlock = rwlock_Init(); +} + +void ldb_write_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_write_rwlock\n"); + /* Don't lock for local database -- let ldapsdk handle thread safety*/ + if (!ldapu_is_local_db(ldb)) + rwlock_WriteLock(lock); +} + +void ldb_read_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_read_rwlock\n"); + /* Don't lock for local database -- let ldapsdk handle thread safety*/ + if (!ldapu_is_local_db(ldb)) + rwlock_ReadLock(lock); +} + +void ldb_unlock_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_unlock_rwlock\n"); + /* we don't lock for local database */ + if (!ldapu_is_local_db(ldb)) + rwlock_Unlock(lock); +} + +int ACL_NeedLDAPOverSSL () +{ + return need_ldap_over_ssl; +} + +NSAPI_PUBLIC int parse_ldap_url (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist, void **db) +{ + LDAPDatabase_t *ldb; + char *binddn = 0; + char *bindpw = 0; + int rv; + + *db = 0; + + if (!url || !*url) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5800, ACL_Program, 1, XP_GetAdminStr(DBT_ldapaclDatabaseUrlIsMissing)); + return -1; + } + + if (!dbname || !*dbname) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5810, ACL_Program, 1, XP_GetAdminStr(DBT_ldapaclDatabaseNameIsMissing)); + return -1; + } + + /* look for binddn and bindpw in the plist */ + if (plist) { + PListFindValue(plist, LDAPU_ATTR_BINDDN, (void **)&binddn, NULL); + PListFindValue(plist, LDAPU_ATTR_BINDPW, (void **)&bindpw, NULL); + } + + rv = ldapu_url_parse(url, binddn, bindpw, &ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5820, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclErrorParsingLdapUrl), ldapu_err2string(rv)); + return -1; + } + + /* success */ + *db = ldb; + + /* Check if we need to do LDAP over SSL */ + if (!need_ldap_over_ssl) { + need_ldap_over_ssl = ldb->use_ssl; + } + + return 0; +} + +int get_is_valid_password_basic_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + /* If the raw-user-name and raw-user-password attributes are present then + * verify the password against the LDAP database. + * Otherwise call AttrGetter for raw-user-name. + */ + char *raw_user; + char *raw_pw; + char *userdn = 0; + int rv; + char *dbname; + ACLDbType_t dbtype; + LDAPDatabase_t *ldb; + time_t *req_time = 0; + pool_handle_t *subj_pool = PListGetPool(subject) + + DBG_PRINT1("get_is_valid_password_basic_ldap\n"); + rv = ACL_GetAttribute(errp, ACL_ATTR_RAW_USER, (void **)&raw_user, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_RAW_PASSWORD, (void **)&raw_pw, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + if (!raw_pw || !*raw_pw) { + /* Null password is not allowed in LDAP since most LDAP servers let + * the bind call succeed as anonymous login (with limited privileges). + */ + return LAS_EVAL_FALSE; + } + + /* Authenticate the raw_user and raw_pw against LDAP database. */ + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + /* We have user name and password. */ + /* Check the cache to see if the password is valid */ + rv = acl_usr_cache_passwd_check(raw_user, dbname, raw_pw, *req_time, + &userdn, subj_pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv != LAS_EVAL_TRUE) { + LDAPMessage *res = 0; + const char *some_attrs[] = { "C", 0 }; + LDAP *ld; + char *udn; + /* Not found in the cache */ + + /* Since we will bind with the user/password and other code relying on + * ldb being bound as ldb->binddn and ldb->bindpw may fail. So block + * them until we are done. + */ + ldb_write_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + ld = ldb->ld; + LDAPU_REQ(rv, ldb, ldapu_find_uid_attrs(ld, raw_user, + ldb->basedn, some_attrs, + 1, &res)); + + if (rv == LDAPU_SUCCESS) { + LDAPMessage *entry = ldap_first_entry(ld, res); + + userdn = ldap_get_dn(ld, entry); + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + LDAPU_REQ(rv, ldb, ldapu_auth_userdn_password(ld, userdn, raw_pw)); + + /* Make sure we rebind with the server's DN + * ignore errors from ldapu_ldap_rebind -- we will get the same + * errors in subsequent calls to LDAP. Return status from the + * above call is our only interest now. + */ + ldapu_ldap_rebind(ldb); + } + + if (res) ldap_msgfree(res); + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_FAILED || rv == LDAP_INVALID_CREDENTIALS) { + /* user entry not found or incorrect password */ + if (userdn) ldap_memfree(userdn); + return LAS_EVAL_FALSE; + } + else if (rv != LDAPU_SUCCESS) { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + if (userdn) ldap_memfree(userdn); + return LAS_EVAL_FAIL; + } + + /* Make an entry in the cache */ + if (acl_usr_cache_enabled()) { + acl_usr_cache_insert(raw_user, dbname, userdn, raw_pw, 0, 0, + *req_time); + } + udn = pool_strdup(subj_pool, userdn); + ldap_memfree(userdn); + userdn = udn; + } + + PListInitProp(subject, ACL_ATTR_IS_VALID_PASSWORD_INDEX, ACL_ATTR_IS_VALID_PASSWORD, raw_user, 0); + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, userdn, 0); + return LAS_EVAL_TRUE; +} + +static int acl_grpcmpfn (const void *groupids, const char *group, + const int len) +{ + const char *token = (const char *)groupids; + int tlen; + char delim = ','; + + while((token = acl_next_token_len(token, delim, &tlen)) != NULL) { + if (tlen > 0 && tlen == len && !strncmp(token, group, len)) + return LDAPU_SUCCESS; + else if (tlen == 0 || 0 != (token = strchr(token+tlen, delim))) + token++; + else + break; + } + + return LDAPU_FAILED; +} + +int get_user_ismember_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + int retval; + int rv; + char *userdn; + char *groups; + char *member_of = 0; + LDAPDatabase_t *ldb; + char *dbname; + ACLDbType_t dbtype; + + DBG_PRINT1("get_user_ismember_ldap\n"); + + rv = ACL_GetAttribute(errp, ACL_ATTR_USERDN, (void **)&userdn, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return LAS_EVAL_FAIL; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_GROUPS, (void **)&groups, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR5900, ACL_Program, 2, XP_GetAdminStr(DBT_GetUserIsMemberLdapUnabelToGetDatabaseName), rv_str); + return rv; + } + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5910, ACL_Program, 2, XP_GetAdminStr(DBT_GetUserIsMemberLdapUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5930, ACL_Program, 2, + XP_GetAdminStr(DBT_GetUserIsMemberLdapCouldntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + /* check if the user is member of any of the groups */ + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + LDAPU_REQ(rv, ldb, ldapu_auth_userdn_groupids(ldb->ld, + userdn, + groups, + acl_grpcmpfn, + ldb->basedn, + &member_of)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* User is a member of one of the groups */ + if (member_of) { + PListInitProp(subject, ACL_ATTR_USER_ISMEMBER_INDEX, + ACL_ATTR_USER_ISMEMBER, + pool_strdup(PListGetPool(subject), member_of), 0); + retval = LAS_EVAL_TRUE; + } + else { + /* This shouldn't happen */ + retval = LAS_EVAL_FALSE; + } + } + else if (rv == LDAPU_FAILED) { + /* User is not a member of any of the groups */ + retval = LAS_EVAL_FALSE; + } + else { + /* unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5950, ACL_Program, 2, + XP_GetAdminStr(DBT_GetUserIsMemberLdapError), + ldapu_err2string(rv)); + retval = LAS_EVAL_FAIL; + } + + return retval; +} + + +/* This function returns LDAPU error codes so that the caller can call + * ldapu_err2string to get the error string. + */ +int acl_map_cert_to_user (NSErr_t *errp, const char *dbname, + LDAPDatabase_t *ldb, void *cert, + PList_t resource, pool_handle_t *pool, + char **user, char **userdn) +{ + int rv; + LDAPMessage *res; + LDAPMessage *entry; + char *uid; + time_t *req_time = 0; + + if (acl_usr_cache_enabled()) { + req_time = acl_get_req_time(resource); + + rv = acl_cert_cache_get_uid (cert, dbname, *req_time, user, userdn, + pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv != LAS_EVAL_TRUE) { + /* Not found in the cache */ + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + /* it sets the variable rv */ + if (rv == LDAPU_SUCCESS) { + char *dn = 0; + + LDAPU_REQ(rv, ldb, ldapu_cert_to_user(cert, ldb->ld, ldb->basedn, + &res, &uid)); + + if (rv == LDAPU_SUCCESS) { + char *dn; + + *user = pool_strdup(pool, uid); + if (!*user) rv = LDAPU_ERR_OUT_OF_MEMORY; + free(uid); + + entry = ldap_first_entry(ldb->ld, res); + dn = ldap_get_dn(ldb->ld, entry); + if (acl_usr_cache_enabled()) { + acl_cert_cache_insert (cert, dbname, *user, dn, *req_time); + } + *userdn = dn ? pool_strdup(pool, dn) : 0; + if (!*userdn) rv = LDAPU_ERR_OUT_OF_MEMORY; + ldap_memfree(dn); + } + if (res) ldap_msgfree(res); + } + ldb_unlock_rwlock(ldb, ldb_rwlock); + } + else { + rv = LDAPU_SUCCESS; + } + + return rv; +} + + +/* + * ACL_LDAPDatabaseHandle - + * Finds the internal structure representing the 'dbname'. If it is an LDAP + * database, returns the 'LDAP *ld' pointer. Also, binds to the LDAP server. + * The LDAP *ld handle can be used in calls to LDAP API. + * Returns LAS_EVAL_TRUE if successful, otherwise logs an error in + * LOG_SECURITY and returns LAS_EVAL_FAIL. + */ +int ACL_LDAPDatabaseHandle (NSErr_t *errp, const char *dbname, LDAP **ld, + char **basedn) +{ + int rv; + ACLDbType_t dbtype; + void *db; + LDAPDatabase_t *ldb; + + *ld = 0; + if (!dbname || !*dbname) dbname = DBCONF_DEFAULT_DBNAME; + + /* Check if the ldb is already in the ACLUserLdbHash */ + ldb = (LDAPDatabase_t *)PR_HashTableLookup(ACLUserLdbHash, dbname); + + if (!ldb) { + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, &db); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR6000, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleNotARegisteredDatabase), dbname); + return LAS_EVAL_FAIL; + } + + if (!ACL_DbTypeIsEqual(errp, dbtype, ACL_DbTypeLdap)) { + /* Not an LDAP database -- error */ + nserrGenerate(errp, ACLERRINVAL, ACLERR6010, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleNotAnLdapDatabase), dbname); + return LAS_EVAL_FAIL; + } + + ldb = ldapu_copy_LDAPDatabase_t((LDAPDatabase_t *)db); + + if (!ldb) { + /* Not an LDAP database -- error */ + nserrGenerate(errp, ACLERRNOMEM, ACLERR6020, ACL_Program, 1, XP_GetAdminStr(DBT_LdapDatabaseHandleOutOfMemory)); + return LAS_EVAL_FAIL; + } + + PR_HashTableAdd(ACLUserLdbHash, PERM_STRDUP(dbname), ldb); + } + + if (!ldb->ld) { + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR6030, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleCouldntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + } + + /* + * Force the rebind -- we don't know whether the customer has used this ld + * to bind as somebody else. It will also check if the LDAP server is up + * and running, reestablish the connection if the LDAP server has rebooted + * since it was last used. + */ + rv = ldapu_ldap_rebind(ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR6040, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleCouldntBindToLdapServer), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + *ld = ldb->ld; + + if (basedn) { + /* They asked for the basedn too */ + *basedn = PERM_STRDUP(ldb->basedn); + } + + return LAS_EVAL_TRUE; +} + +int get_userdn_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + char *uid; + char *dbname; + char *userdn; + time_t *req_time = 0; + pool_handle_t *subj_pool = PListGetPool(subject); + int rv; + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return LAS_EVAL_FAIL; + } + + /* The getter for ACL_ATTR_USER may have put the USERDN on the PList */ + rv = PListGetValue(subject, ACL_ATTR_USERDN_INDEX, (void **)&userdn, NULL); + + if (rv >= 0) { + /* Nothing to do */ + return LAS_EVAL_TRUE; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + /* Check if the userdn is available in the usr_cache */ + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + rv = acl_usr_cache_get_userdn(uid, dbname, *req_time, &userdn, + subj_pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv == LAS_EVAL_TRUE) { + /* Found in the cache */ + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + userdn, 0); + } + else { + ACLDbType_t dbtype; + LDAPDatabase_t *ldb = 0; + + /* Perform LDAP lookup */ + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + LDAPU_REQ(rv, ldb, ldapu_find_userdn(ldb->ld, uid, ldb->basedn, + &userdn)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* Found it. Store it in the cache also. */ + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + pool_strdup(subj_pool, userdn), 0); + if (acl_usr_cache_enabled()) { + acl_usr_cache_set_userdn(uid, dbname, userdn, *req_time); + } + ldapu_free(userdn); + rv = LAS_EVAL_TRUE; + } + else if (rv == LDAPU_FAILED) { + /* Not found but not an error */ + rv = LAS_EVAL_FALSE; + } + else { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + rv = LAS_EVAL_FAIL; + } + } + + return rv; +} + +/* Attr getter for LDAP database to check if the user exists */ +int get_user_exists_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + int rv; + char *user; + char *userdn; + + /* See if the userdn is already available */ + rv = PListGetValue(subject, ACL_ATTR_USERDN_INDEX, (void **)&userdn, NULL); + + if (rv >= 0) { + /* Check if the DN is still valid against the database */ + /* Get the database name */ + char *dbname; + ACLDbType_t dbtype; + LDAPDatabase_t *ldb = 0; + LDAPMessage *res; + const char *some_attrs[] = { "c", 0 }; + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + /* Perform LDAP lookup */ + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + LDAPU_REQ(rv, ldb, ldapu_find (ldb->ld, ldb->basedn, LDAP_SCOPE_BASE, + NULL, some_attrs, 1, &res)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* Found it. */ + rv = LAS_EVAL_TRUE; + } + else if (rv == LDAPU_FAILED) { + /* Not found but not an error */ + rv = LAS_EVAL_FALSE; + } + else { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + rv = LAS_EVAL_FAIL; + } + } + else { + /* If the DN doesn't exist, should we just return an error ? */ + /* If yes, we don't need rest of the code */ + + /* If we don't have a DN, we must have a user at least */ + rv = PListGetValue(subject, ACL_ATTR_USER_INDEX, (void **)&user, NULL); + + if (rv < 0) { + /* We don't even have a user name */ + return LAS_EVAL_FAIL; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_USERDN, (void **)&userdn, subject, + resource, auth_info, global_auth); + } + + /* If we can get the userdn then the user exists */ + if (rv == LAS_EVAL_TRUE) { + PListInitProp(subject, ACL_ATTR_USER_EXISTS_INDEX, + ACL_ATTR_USER_EXISTS, userdn, 0); + } + + return rv; +} + +/* acl_user_exists - */ +/* Function to check if the user still exists */ +/* This function works for all kinds of databases */ +/* Returns 0 on success and -ve value on failure */ +NSAPI_PUBLIC int acl_user_exists (const char *user, const char *userdn, + const char *dbname, const int logerr) +{ + NSErr_t err = NSERRINIT; + NSErr_t *errp = &err; + pool_handle_t *pool = 0; + time_t *req_time = 0; + PList_t subject = 0; + PList_t resource = 0; + PList_t auth_info = 0; + PList_t global_auth = NULL; + char *olddn = 0; + int rv; + + /* Check if the userdn is available in the usr_cache */ + if (acl_usr_cache_enabled() && userdn) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = (time_t *)MALLOC(sizeof(time_t)); + + if (req_time) { + time(req_time); + rv = acl_usr_cache_userdn_check(user, dbname, userdn, *req_time); + FREE((void *)req_time); + } + + if (rv == LAS_EVAL_TRUE) + { + /* Found in the cache with the same DN */ + return 0; + } + } + + pool = pool_create(); + subject = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + resource = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + auth_info = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + + if (!pool || !subject || !resource || !auth_info) { + /* ran out of memory */ + goto no_mem; + } + + /* store a pointer to the user rather than a copy */ + rv = PListInitProp(subject, ACL_ATTR_USER_INDEX, ACL_ATTR_USER, + user, 0); + if (rv < 0) { /* Plist error */ goto plist_err; } + + if (userdn && *userdn) { + /* store a pointer to the userdn rather than a copy */ + rv = PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + userdn, 0); + if (rv < 0) { /* Plist error */ goto plist_err; } + } + + /* store the cached dbname on auth_info */ + rv = ACL_AuthInfoSetDbname(errp, auth_info, dbname); + if (rv < 0) { /* auth_info error */ goto err; } + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER_EXISTS, (void **)&user, + subject, resource, auth_info, global_auth); + + if (rv == LAS_EVAL_TRUE) { + /* User still exists */ + rv = 0; + } + else if (rv == LAS_EVAL_FALSE) { + /* User doesn't exist anymore */ + nserrGenerate(errp, ACLERRFAIL, 5880, ACL_Program, 2, XP_GetAdminStr(DBT_AclUserExistsNot), user); + goto err; + } + else { + /* Unexpected error while checking the existence of the user */ + goto err; + } + + goto done; + +plist_err: + nserrGenerate(errp, ACLERRFAIL, 5890, ACL_Program, 1, XP_GetAdminStr(DBT_AclUserPlistError)); + goto err; + +no_mem: + nserrGenerate(errp, ACLERRNOMEM, 5870, ACL_Program, 1, XP_GetAdminStr(DBT_AclUserExistsOutOfMemory)); + goto err; + +err: + if (logerr) { + /* Unexpected error while checking the existence of the user */ + char buf[BIG_LINE]; + /* generate error message (upto depth 6) into buf */ + aclErrorFmt(errp, buf, BIG_LINE, 6); + ereport(LOG_SECURITY, "Error while checking the existence of user: %s", buf); + } + + nserrDispose(errp); + rv = -1; + +done: + /* Destroy the PLists & the pool */ + if (subject) PListDestroy(subject); + if (resource) PListDestroy(resource); + if (auth_info) PListDestroy(auth_info); + if (pool) pool_destroy(pool); + return rv; +} diff --git a/lib/libaccess/ldapauth.h b/lib/libaccess/ldapauth.h new file mode 100644 index 00000000..409aa6e9 --- /dev/null +++ b/lib/libaccess/ldapauth.h @@ -0,0 +1,42 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LDAP_AUTH_H +#define LDAP_AUTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ldap/ldap.h" + +const int LDAP_ACL_SUCCESS = 0; +const int LDAP_ACL_FAILED = -1; + +extern int ldap_auth_userdn_groupdn (LDAP *ld, char *userdn, + char *groupdn); +extern int ldap_auth_uid_groupdn (LDAP *ld, char *uid, + char *groupdn); +extern int ldap_auth_uid_groupid (LDAP *ld, char *uid, + char *groupid); +extern int ldap_auth_userdn_groupid (LDAP *ld, char *userdn, + char *groupid); +extern int ldap_auth_userdn_attrfilter (LDAP *ld, char *userdn, + char *attrfilter); +extern int ldap_auth_uid_attrfilter (LDAP *ld, char *uid, + char *attrfilter); +extern int ldap_auth_userdn_password (LDAP *ld, char *userdn, + char *password); +extern int ldap_find_uid (LDAP *ld, char *uid, LDAPMessage **res); +extern int ldap_auth_uid_password (LDAP *ld, char *uid, + char *password); +extern LDAP *init_ldap(); + +#ifdef __cplusplus +} +#endif + +#endif /* LDAP_AUTH_H */ diff --git a/lib/libaccess/leval.h b/lib/libaccess/leval.h new file mode 100644 index 00000000..22d3ee8a --- /dev/null +++ b/lib/libaccess/leval.h @@ -0,0 +1,18 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LEVAL_H +#define LEVAL_H + +NSPR_BEGIN_EXTERN_C + +int +freeLAS(NSErr_t *errp, char *attribute, void **las_cookie); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/lparse.h b/lib/libaccess/lparse.h new file mode 100644 index 00000000..6386d56e --- /dev/null +++ b/lib/libaccess/lparse.h @@ -0,0 +1,27 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * This grammar is intended to parse the version 3.0 ACL + * and output an ACLParseACE_t structure. + */ + +#ifndef LPARSE_H +#define LPARSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int aclPushListHandle(ACLListHandle_t *handle); +extern int aclparse(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libaccess/method.cpp b/lib/libaccess/method.cpp new file mode 100644 index 00000000..1425ad55 --- /dev/null +++ b/lib/libaccess/method.cpp @@ -0,0 +1,163 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <netsite.h> +#include <libaccess/las.h> +#include <libaccess/acl.h> +#include <libaccess/aclerror.h> +#include <libaccess/dbtlibaccess.h> +#include "aclpriv.h" + +NSAPI_PUBLIC int ACL_ModuleRegister (NSErr_t *errp, const char *module_name, + AclModuleInitFunc func) +{ + int rv; + + if (!module_name || !*module_name) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4200, ACL_Program, 1, + XP_GetAdminStr(DBT_ModuleRegisterModuleNameMissing)); + return -1; + } + + rv = (*func)(errp); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4210, ACL_Program, 2, + XP_GetAdminStr(DBT_ModuleRegisterFailed), module_name); + return rv; + } + + return 0; +} + + +static int attr_getter_is_matching(NSErr_t *errp, ACLAttrGetter_t *getter, + ACLMethod_t method, ACLDbType_t dbtype) +{ + if ((ACL_MethodIsEqual(errp, getter->method, method) || + ACL_MethodIsEqual(errp, getter->method, ACL_METHOD_ANY)) && + (ACL_DbTypeIsEqual(errp, getter->dbtype, dbtype) || + ACL_DbTypeIsEqual(errp, getter->dbtype, ACL_DBTYPE_ANY))) + { + return 1; + } + else { + return 0; + } +} + + +NSAPI_PUBLIC int ACL_GetAttribute(NSErr_t *errp, const char *attr, void **val, + PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + int rv; + void *attrval; + ACLAttrGetterFn_t func; + ACLAttrGetterList_t getters; + ACLAttrGetter_t *getter; + ACLMethod_t method; + ACLDbType_t dbtype; + + /* If subject PList is NULL, we will fail anyway */ + if (!subject) return LAS_EVAL_FAIL; + + /* Is the attribute already present in the subject property list? */ + + rv = PListFindValue(subject, attr, &attrval, NULL); + if (rv >= 0) { + + /* Yes, take it from there */ + *val = attrval; + return LAS_EVAL_TRUE; + } + + /* Get the authentication method and database type */ + + rv = ACL_AuthInfoGetMethod(errp, auth_info, &method); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4300, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeCouldntDetermineMethod), attr); + return LAS_EVAL_FAIL; + } + + rv = ACL_AuthInfoGetDbType (errp, auth_info, &dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4380, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileCouldntDetermineDbtype), attr); + return LAS_EVAL_FAIL; + } + + /* Get the list of attribute getters */ + + rv = ACL_AttrGetterFind(errp, attr, &getters); + + if ((rv < 0) || (getters == 0)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4310, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeCouldntLocateGetter), attr); + return LAS_EVAL_FAIL; + } + + /* Iterate over each getter and see if it should be called + * Call each matching getter until a getter which doesn't decline is + * found. + */ + + for (getter = ACL_AttrGetterFirst(&getters); + getter != 0; + getter = ACL_AttrGetterNext(&getters, getter)) { + + /* Require matching method and database type */ + + if (attr_getter_is_matching(errp, getter, method, dbtype)) { + + /* Call the getter function */ + func = getter->fn; + rv = (*func)(errp, subject, resource, auth_info, global_auth, + getter->arg); + + /* Did the getter succeed? */ + if (rv == LAS_EVAL_TRUE) { + + /* + * Yes, it should leave the attribute on the subject + * property list. + */ + rv = PListFindValue(subject, attr, (void **)&attrval, NULL); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4320, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeDidntSetAttr), attr); + return LAS_EVAL_FAIL; + } + + /* Got it */ + *val = attrval; + return LAS_EVAL_TRUE; + } + + /* Did the getter decline? */ + if (rv != LAS_EVAL_DECLINE) { + + /* No, did it fail to get the attribute */ + if (rv == LAS_EVAL_FAIL || rv == LAS_EVAL_INVALID) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4330, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeDidntGetAttr), attr); + } + + return rv; + } + } + } + + /* If we fall out of the loop, all the getters declined */ + nserrGenerate(errp, ACLERRFAIL, ACLERR4340, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeAllGettersDeclined), attr); + return LAS_EVAL_FAIL; +} + diff --git a/lib/libaccess/nsadb.cpp b/lib/libaccess/nsadb.cpp new file mode 100644 index 00000000..a87951b8 --- /dev/null +++ b/lib/libaccess/nsadb.cpp @@ -0,0 +1,582 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsadb.c) + * + * This module contains routines for retrieving information from + * a Netscape authentication database. An authentication database + * consists of a user database and a group database. This module + * implements an authentication database based on Netscape user and + * group databases defined in nsuser.h and nsgroup.h, which in turn + * are based on the Netscape (server) database implementation + * defined in nsdb.h. The interface for managing information in + * an authentication database is described separately in nsamgmt.h. + */ + +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#include <base/fsmutex.h> +#include <libaccess/nsdbmgmt.h> +#define __PRIVATE_NSADB +#include <libaccess/nsadb.h> +#include <libaccess/nsuser.h> +#include <libaccess/nsgroup.h> + +/* + * Description (NSADB_AuthIF) + * + * This structure defines a generic authentication database + * interface for this module. It does not currently support + * user/group id lookup. + */ +AuthIF_t NSADB_AuthIF = { + 0, /* find user/group by id */ + nsadbFindByName, /* find user/group by name */ + nsadbIdToName, /* lookup name for user/group id */ + nsadbOpen, /* open a named database */ + nsadbClose, /* close a database */ +}; + +/* + * Description (nsadbClose) + * + * This function closes an authentication database previously opened + * via nsadbOpen(). + * + * Arguments: + * + * authdb - handle returned by nsadbOpen() + * flags - unused (must be zero) + */ + +NSAPI_PUBLIC void nsadbClose(void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + + if (adb->adb_userdb != 0) { + ndbClose(adb->adb_userdb, 0); + } + + if (adb->adb_groupdb != 0) { + ndbClose(adb->adb_groupdb, 0); + } + +#if defined(CLIENT_AUTH) + nsadbCloseCerts(authdb, flags); +#endif + + if (adb->adb_dbname) { + FREE(adb->adb_dbname); + } + + FREE(adb); +} + +/* + * Description (nsadbOpen) + * + * This function is used to open an authentication database. + * The caller specifies a name for the database, which is actually + * the name of a directory containing the files which comprise the + * database. The caller also indicates whether this is a new + * database, in which case it is created. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * adbname - name of this database (directory) + * flags - open flags: + * AIF_CREATE - new database (create) + * rptr - pointer to returned handle + * + * Returns: + * + * A handle for accessing the database is always returned via 'rptr' + * unless there was a shortage of dynamic memory, in which case a + * null handle is returned. The return value of the function is + * 0 if it completes successfully. An error is indicated by a + * negative return value (see nsautherr.h). + */ + +NSAPI_PUBLIC int nsadbOpen(NSErr_t * errp, + char * adbname, int flags, void **rptr) +{ + AuthDB_t * authdb = 0; /* pointer to database descriptor */ + SYS_DIR dbdir; /* database directory handle */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Make sure we have a place to return the database handle */ + if (rptr == 0) goto err_inval; + + /* Allocate the database descriptor */ + authdb = (AuthDB_t *)MALLOC(sizeof(AuthDB_t)); + if (authdb == 0) goto err_nomem; + + /* Return the descriptor pointer as the database handle */ + *rptr = (void *)authdb; + + authdb->adb_dbname = STRDUP(adbname); + authdb->adb_userdb = 0; + authdb->adb_groupdb = 0; +#if defined(CLIENT_AUTH) + authdb->adb_certdb = 0; + authdb->adb_certlock = 0; + authdb->adb_certnm = 0; +#endif + authdb->adb_flags = 0; + + /* See if the database directory exists */ + dbdir = dir_open(adbname); + if (dbdir == 0) { + /* No, create it if this is a new database, else error */ + if (flags & AIF_CREATE) { + rv = dir_create(adbname); + if (rv < 0) goto err_mkdir; + authdb->adb_flags |= ADBF_NEW; + } + else goto err_dopen; + } + else { + /* Ok, it's there */ + dir_close(dbdir); + } + + return 0; + + err_inval: + eid = NSAUERR3000; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + /* Error - insufficient dynamic memory */ + eid = NSAUERR3020; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_mkdir: + eid = NSAUERR3040; + rv = NSAERRMKDIR; + goto err_dir; + + err_dopen: + eid = NSAUERR3060; + rv = NSAERROPEN; + goto err_dir; + + err_dir: + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adbname); + goto punt; + + punt: + /* Fatal error - free database descriptor and return null handle */ + if (authdb) { + if (authdb->adb_dbname) { + FREE(authdb->adb_dbname); + } + FREE(authdb); + } + + if (rptr) *rptr = 0; + + return rv; +} + +/* + * Description (nsadbOpenUsers) + * + * This function is called to open the users subdatabase of an + * open authentication database. The caller specifies flags to + * indicate whether read or write access is required. This + * function is normally called only by routines below the + * nsadbOpen() API, in response to perform particular operations + * on user or group objects. If the open is successful, the + * resulting handle is stored in the AuthDB_t structure. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - open flags: + * ADBF_UREAD - open for read + * ADBF_UWRITE - open for read/write + * Returns: + * + * The return value is zero if the operation is successfully + * completed. An error is indicated by a negative return value + * (see nsautherr.h), and an error frame is generated if an error + * frame list was provided. + */ + +NSAPI_PUBLIC int nsadbOpenUsers(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * userfn = 0; /* user database name */ + int dblen; /* strlen(adb_dbname) */ + int uversion; /* user database version number */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == 0) goto err_inval; + + /* Is the user database already open? */ + if (adb->adb_userdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + + /* We need to open the database */ + + /* Allocate space for the user database filename */ + dblen = strlen(adb->adb_dbname); + + userfn = (char *)MALLOC(dblen + strlen(ADBUSERDBNAME) + 2); + if (userfn == 0) goto err_nomem; + + /* Construct user database name */ + strcpy(userfn, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (userfn[dblen-1] != FILE_PATHSEP) { + userfn[dblen] = FILE_PATHSEP; + userfn[dblen+1] = 0; + ++dblen; + } + + strcpy(&userfn[dblen], ADBUSERDBNAME); + + adb->adb_userdb = ndbOpen(errp, + userfn, 0, NDB_TYPE_USERDB, &uversion); + if (adb->adb_userdb == 0) goto err_uopen; + + FREE(userfn); + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_UREAD|ADBF_UWRITE); + if (flags & ADBF_UWRITE) adb->adb_flags |= ADBF_UWRITE; + else adb->adb_flags |= ADBF_UREAD; + + return 0; + + err_inval: + eid = NSAUERR3200; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3220; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_uopen: + eid = NSAUERR3240; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, userfn); + goto punt; + + punt: + return rv; +} + +/* + * Description (nsadbOpenGroups) + * + * This function is called to open the groups subdatabase of an + * open authentication database. The caller specifies flags to + * indicate whether read or write access is required. This + * function is normally called only by routines below the + * nsadbOpen() API, in response to perform particular operations + * on user or group objects. If the open is successful, the + * resulting handle is stored in the AuthDB_t structure. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - open flags: + * ADBF_GREAD - open for read + * ADBF_GWRITE - open for read/write + * Returns: + * + * The return value is zero if the operation is successfully + * completed. An error is indicated by a negative return value + * (see nsautherr.h), and an error frame is generated if an error + * frame list was provided. + */ + +NSAPI_PUBLIC int nsadbOpenGroups(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * groupfn = 0; /* group database name */ + int dblen; /* strlen(adb_dbname) */ + int gversion; /* group database version number */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == 0) goto err_inval; + + /* Is the group database already open? */ + if (adb->adb_groupdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + + /* We need to open the database */ + + /* Allocate space for the group database filename */ + dblen = strlen(adb->adb_dbname); + + groupfn = (char *)MALLOC(dblen + strlen(ADBGROUPDBNAME) + 2); + if (groupfn == 0) goto err_nomem; + + /* Construct group database name */ + strcpy(groupfn, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (groupfn[dblen-1] != FILE_PATHSEP) { + groupfn[dblen] = FILE_PATHSEP; + groupfn[dblen+1] = 0; + ++dblen; + } + + strcpy(&groupfn[dblen], ADBGROUPDBNAME); + + adb->adb_groupdb = ndbOpen(errp, + groupfn, 0, NDB_TYPE_GROUPDB, &gversion); + if (adb->adb_groupdb == 0) goto err_gopen; + + FREE(groupfn); + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_GREAD|ADBF_GWRITE); + if (flags & ADBF_GWRITE) adb->adb_flags |= ADBF_GWRITE; + else adb->adb_flags |= ADBF_GREAD; + + return 0; + + err_inval: + eid = NSAUERR3300; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3320; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_gopen: + eid = NSAUERR3340; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, groupfn); + goto punt; + + punt: + return rv; +} + +/* + * Description (nsadbIdToName) + * + * This function looks up a specified user or group id in the + * authentication database. The name associated with the specified + * id is returned. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * id - user or group id + * flags - AIF_USER or AIF_GROUP (defined in nsauth.h) + * rptr - pointer to returned group or user name + * + * Returns: + * + * The return value is zero if no error occurs, + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbIdToName(NSErr_t * errp, + void * authdb, USI_t id, int flags, char **rptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + void * whichdb = 0; + char * name; + int rv; + + if (rptr != 0) *rptr = 0; + + /* Decide whether to use user or group database */ + if (flags & AIF_USER) { + + whichdb = adb->adb_userdb; + if (whichdb == 0) { + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + whichdb = adb->adb_userdb; + } + } + else if (flags & AIF_GROUP) { + + whichdb = adb->adb_groupdb; + if (whichdb == 0) { + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + whichdb = adb->adb_groupdb; + } + } + + if (whichdb != 0) { + + /* Get the name corresponding to the id */ + rv = ndbIdToName(errp, whichdb, id, 0, &name); + if (rv < 0) goto punt; + + if ((rptr != 0)) *rptr = name; + rv = 0; + } + + punt: + return rv; +} + +/* + * Description (nsadbFindByName) + * + * This function looks up a specified name in the authentication + * database. Flags specified by the caller indicate whether a + * group name, user name, or either should be found. The caller + * may optionally provide for the return of a user or group object + * pointer, in which case the information associated with a + * matching group or user is used to create a group or user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * name - name of group or user + * flags - search flags (defined in nsauth.h) + * rptr - pointer to returned group or user + * object pointer (may be null) + * + * Returns: + * + * The return value is a non-negative value if no error occurs, + * and the value indicates whether the name matched a group or + * user: + * + * AIF_NONE - name did not match a group or user name + * AIF_GROUP - name matched a group name + * AIF_USER - name matched a user name + * + * If the value is AIF_GROUP or AIF_USER, and rptr is non-null, + * then a group or user object is created, and a pointer to it is + * returned in the location indicated by rptr. + * + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbFindByName(NSErr_t * errp, void * authdb, + char * name, int flags, void **rptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t recptr; + int reclen; + int rv; + + if (rptr != 0) *rptr = 0; + + /* Search for group name? */ + if (flags & AIF_GROUP) { + + if (adb->adb_groupdb == 0) { + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + } + + /* Look up the name in the group database */ + rv = ndbFindName(errp, adb->adb_groupdb, 0, (char *)name, + &reclen, (char **)&recptr); + if (rv == 0) { + + /* Found it. Make a group object if requested. */ + if (rptr != 0) { + + /* Got the group record. Decode into a group object. */ + *rptr = (void *)groupDecode((NTS_t)name, reclen, recptr); + } + + return AIF_GROUP; + } + } + + /* Search for user name? */ + if (flags & AIF_USER) { + + if (adb->adb_userdb == 0) { + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + } + + /* Look up the name in the user database */ + rv = ndbFindName(errp, adb->adb_userdb, 0, (char *)name, + &reclen, (char **)&recptr); + if (rv == 0) { + + /* Found it. Make a user object if requested. */ + if (rptr != 0) { + + /* Got the user record. Decode into a user object. */ + *rptr = (void *)userDecode((NTS_t)name, reclen, recptr); + } + + return AIF_USER; + } + } + + /* Nothing found */ + nserrDispose(errp); + return AIF_NONE; + + punt: + return rv; +} diff --git a/lib/libaccess/nsamgmt.cpp b/lib/libaccess/nsamgmt.cpp new file mode 100644 index 00000000..544d3617 --- /dev/null +++ b/lib/libaccess/nsamgmt.cpp @@ -0,0 +1,1567 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsamgmt.c) + * + * This module contains routines for managing information in a + * Netscape authentication database. An authentication database + * consists of a user database and a group database. This module + * implements an authentication database based on Netscape user and + * group databases defined in nsuser.h and nsgroup.h, which in turn + * are based on the Netscape (server) database implementation + * defined in nsdb.h. The interface for retrieving information + * from an authentication database is described separately in + * nsadb.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "base/file.h" +#define __PRIVATE_NSADB +#include "libaccess/nsamgmt.h" +#include "libaccess/nsumgmt.h" +#include "libaccess/nsgmgmt.h" + +/* + * Description (nsadbEnumUsersHelp) + * + * This is a local function that is called by NSDB during user + * database enumeration. It decodes user records into user + * objects, and presents them to the caller of nsadbEnumerateUsers(), + * via the specified call-back function. The call-back function + * return value may be a negative error code, which will cause + * enumeration to stop, and the error code will be returned from + * nsadbEnumerateUsers(). If the return value of the call-back + * function is not negative, it can contain one or more of the + * following flags: + * + * ADBF_KEEPOBJ - do not free the UserObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to UserEnumArgs_t structure + * namelen - user record key length including null + * terminator + * name - user record key (user account name) + * reclen - length of user record + * recptr - pointer to user record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct EnumUserArgs_s EnumUserArgs_t; +struct EnumUserArgs_s { + void * authdb; + int (*func)(NSErr_t * ferrp, + void * authdb, void * argp, UserObj_t * uoptr); + void * user; + int rv; +}; + +static int nsadbEnumUsersHelp(NSErr_t * errp, void * parg, + int namelen, char * name, + int reclen, char * recptr) +{ + EnumUserArgs_t * ue = (EnumUserArgs_t *)parg; + UserObj_t * uoptr; /* user object pointer */ + int rv; + + uoptr = userDecode((NTS_t)name, reclen, (ATR_t)recptr); + if (uoptr != 0) { + rv = (*ue->func)(errp, ue->authdb, ue->user, uoptr); + if (rv >= 0) { + + /* Count the number of users seen */ + ue->rv += 1; + + /* Free the user object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + userFree(uoptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* Free the user object in the event of an error */ + userFree(uoptr); + + /* Also return the error code */ + ue->rv = rv; + } + } + + return rv; +} + +/* + * Description (nsadbEnumGroupsHelp) + * + * This is a local function that is called by NSDB during group + * database enumeration. It decodes group records into group + * objects, and presents them to the caller of nsadbEnumerateGroups(), + * via the specified call-back function. The call-back function + * return value may be a negative error code, which will cause + * enumeration to stop, and the error code will be returned from + * nsadbEnumerateGroups(). If the return value of the call-back + * function is not negative, it can contain one or more of the + * following flags: + * + * ADBF_KEEPOBJ - do not free the GroupObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to GroupEnumArgs_t structure + * namelen - group record key length including null + * terminator + * name - group record key (group name) + * reclen - length of group record + * recptr - pointer to group record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct EnumGroupArgs_s EnumGroupArgs_t; +struct EnumGroupArgs_s { + void * authdb; + int (*func)(NSErr_t * ferrp, + void * authdb, void * argp, GroupObj_t * goptr); + void * user; + int rv; +}; + +static int nsadbEnumGroupsHelp(NSErr_t * errp, void * parg, + int namelen, char * name, + int reclen, char * recptr) +{ + EnumGroupArgs_t * eg = (EnumGroupArgs_t *)parg; + GroupObj_t * goptr; /* group object pointer */ + int rv; + + goptr = groupDecode((NTS_t)name, reclen, (ATR_t)recptr); + if (goptr != 0) { + rv = (*eg->func)(errp, eg->authdb, eg->user, goptr); + if (rv >= 0) { + + /* Count the number of groups seen */ + eg->rv += 1; + + /* Free the group object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + groupFree(goptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* Free the group object in the event of an error */ + groupFree(goptr); + + /* Also return the error code */ + eg->rv = rv; + } + } + + return rv; +} + +NSPR_BEGIN_EXTERN_C + +/* + * Description (nsadbAddGroupToGroup) + * + * This function adds a child group, C, to the definition of a + * parent group P. This involves updating the group entries of + * C and P in the group database. It also involves updating + * the group lists of any user descendants of C, to reflect the + * fact that these users are now members of P and P's ancestors. + * A check is made for an attempt to create a cycle in the group + * hierarchy, and this is rejected as an error. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * pgoptr - pointer to parent group object + * cgoptr - pointer to child group object + * + * Returns: + * + * The return value is zero if group C was not already a direct + * member of group P, and was added successfully. A return value + * of +1 indicates that group C was already a direct member of + * group P. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbAddGroupToGroup(NSErr_t * errp, void * authdb, + GroupObj_t * pgoptr, GroupObj_t * cgoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t gsuper; /* list of ancestors of group P */ + USIList_t dglist; /* descendant groups of C */ + GroupObj_t * dgoptr; /* descendant group object pointer */ + UserObj_t * uoptr; /* user object pointer */ + USI_t id; /* current descendant group id */ + int usercount; /* count of users for descendant */ + USI_t * userlist; /* pointer to array of descendant user ids */ + USI_t * idlist; /* pointer to array of descendant group ids */ + int pass; /* loop pass number */ + int i; /* loop index */ + int rv; /* result value */ + + /* Is C a direct member of P already? */ + if (usiPresent(&pgoptr->go_groups, cgoptr->go_gid)) { + /* Yes, indicate that */ + return 0; + } + + dgoptr = 0; + uoptr = 0; + + /* Initialize a list of the group descendants of group C */ + UILINIT(&dglist); + + /* Initialize a list of P and its ancestors */ + UILINIT(&gsuper); + + /* Add P to the ancestor list */ + rv = usiInsert(&gsuper, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Merge all the ancestors of group P into the list */ + rv = nsadbSuperGroups(errp, authdb, pgoptr, &gsuper); + if (rv < 0) goto punt; + + /* + * Each pass through the following loop visits C and all of C's + * descendant groups. + * + * The first pass checks to see if making group C a member of + * group P would create a cycle in the group structure. It does + * this by examining C and all of its dependents to see if any + * appear in the list containing P and P's ancestors. + * + * The second pass updates the group lists of all users contained + * in group C to include P and P's ancestors. + */ + + for (pass = 1; pass < 3; ++pass) { + + /* Use the group C as the first descendant */ + id = cgoptr->go_gid; + dgoptr = cgoptr; + + for (;;) { + + if (pass == 1) { + /* + * Check for attempt to create a cycle in the group + * hierarchy. See if this descendant is a member of + * the list of P and P's ancestors (gsuper). + */ + if (usiPresent(&gsuper, id)) { + /* + * Error - operation would create a cycle + * in the group structure. + */ + return -1; + } + } + else { + + /* + * Merge the list of ancestors of P (gsuper) with the + * group lists of any direct user members of the current + * descendant group, referenced by dgoptr. + */ + + /* Get direct user member list size and pointer */ + usercount = UILCOUNT(&dgoptr->go_users); + userlist = UILLIST(&dgoptr->go_users); + + /* For each direct user member of this descendant ... */ + for (i = 0; i < usercount; ++i) { + + /* Get a user object for the user */ + uoptr = userFindByUid(errp, + adb->adb_userdb, userlist[i]); + if (uoptr == 0) { + /* + * Error - user not found, + * databases are inconsistent. + */ + rv = -1; + goto punt; + } + + /* Merge gsuper into the user's group list */ + rv = uilMerge(&uoptr->uo_groups, &gsuper); + if (rv < 0) goto punt; + + /* Write out the user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv) goto punt; + + /* Free the user object */ + userFree(uoptr); + uoptr = 0; + } + } + + /* + * Merge the direct member groups of the current descendant + * group into the list of descendants to be processed. + */ + rv = uilMerge(&dglist, &dgoptr->go_groups); + if (rv < 0) goto punt; + + /* Free the group object for the current descendant */ + if (dgoptr != cgoptr) { + groupFree(dgoptr); + dgoptr = 0; + } + + /* Exit the loop if the descendant list is empty */ + if (UILCOUNT(&dglist) <= 0) break; + + /* Otherwise remove the next descendant from the list */ + idlist = UILLIST(&dglist); + id = idlist[0]; + rv = usiRemove(&dglist, id); + if (rv < 0) goto punt; + + /* Now get a group object for this descendant group */ + dgoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (dgoptr == 0) { + /* Error - group not found, databases are inconsistent */ + rv = -1; + goto punt; + } + } + } + + /* Now add C to P's list of member groups */ + rv = usiInsert(&pgoptr->go_groups, cgoptr->go_gid); + if (rv < 0) goto punt; + + /* Add P to C's list of parent groups */ + rv = usiInsert(&cgoptr->go_pgroups, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Update the database entry for group C */ + cgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, cgoptr); + if (rv) goto punt; + + /* Update the database entry for group P */ + pgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, pgoptr); + + return rv; + + punt: + /* Handle errors */ + UILFREE(&gsuper); + UILFREE(&dglist); + if (dgoptr) { + groupFree(dgoptr); + } + if (uoptr) { + userFree(uoptr); + } + return rv; +} + +/* + * Description (nsadbAddUserToGroup) + * + * This function adds a user to a group definition. This involves + * updating the group entry in the group database, and the user + * entry in the user database. The caller provides a pointer to + * a user object for the user to be added, a pointer to a group + * object for the group being modified, and a handle for the + * authentication databases (from nsadbOpen()). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * uoptr - pointer to user object + * + * Returns: + * + * The return value is zero if the user was not already a direct + * member of the group, and was added successfully. A return value + * of +1 indicates that the user was already a direct member of the + * group. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbAddUserToGroup(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t nglist; /* new group list for specified user */ + USIList_t gsuper; /* groups containing+ the specified group */ + GroupObj_t * aoptr; /* group object for 'id' group */ + USI_t * idlist; /* pointer to gsuper gid array */ + USI_t id; /* current gid from gsuper */ + int rv; /* result value */ + + /* Is the user already a direct member of the group? */ + if (usiPresent(&goptr->go_users, uoptr->uo_uid)) { + + /* Yes, nothing to do */ + return 1; + } + + /* + * The user object contains a list of all of the groups that contain + * the user, either directly or indirectly. We need to add the + * specified group and its ancestors to this list. Each group contains + * a list of the group's parents, which is used to locate all of the + * group's ancestors. As an optimization, we need not consider any + * ancestors which are already on the user's current group list. + */ + + /* + * The following loop will deal with two lists of group ids. One + * is the list that will become the new group list for the user, + * which is initialized to the user's current group list. The other + * is a list of ancestors of the group to be considered for addition + * to the user's group list. This list is initialized to the specified + * group. + */ + + /* Initialize both lists to be empty */ + UILINIT(&nglist); + UILINIT(&gsuper); + + /* Make a copy of the user's current group list */ + rv = uilDuplicate(&nglist, &uoptr->uo_groups); + if (rv < 0) goto punt; + + /* Start the other list with the specified group */ + rv = usiInsert(&gsuper, goptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* While entries remain on the ancestor list */ + while (UILCOUNT(&gsuper) > 0) { + + /* Get pointer to array of ancestor group ids */ + idlist = UILLIST(&gsuper); + + /* Remove the first ancestor */ + id = idlist[0]; + usiRemove(&gsuper, id); + + /* Is the ancestor on the user's current group list? */ + if (!usiPresent(&uoptr->uo_groups, id)) { + + /* No, add its parents to the ancestor list */ + + /* Look up the ancestor group (get a group object for it) */ + aoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (aoptr == 0) { + /* Error - group not found, database inconsistent */ + rv = -1; + goto punt; + } + + /* Merge the ancestors parents into the ancestor list */ + rv = uilMerge(&gsuper, &aoptr->go_pgroups); + + /* Lose the ancestor group object */ + groupFree(aoptr); + + /* See if the merge worked */ + if (rv < 0) goto punt; + } + + /* Add the ancestor to the new group list for the user */ + rv = usiInsert(&nglist, id); + if (rv < 0) goto punt; + } + + /* Add the user to the group's user member list */ + rv = usiInsert(&goptr->go_users, uoptr->uo_uid); + if (rv < 0) goto punt; + + /* Replace the user's group list with the new one */ + UILREPLACE(&uoptr->uo_groups, &nglist); + + /* Write out the updated user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv < 0) goto punt; + + /* Write out the updated group object */ + goptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + return rv; + + punt: + /* Handle error */ + + /* Free ancestor and new group lists */ + UILFREE(&nglist); + UILFREE(&gsuper); + + return rv; +} + +/* + * Description (nsadbCreateGroup) + * + * This function creates a new group in a specified authentication + * database. The group is described by a group object. A group + * object can be created by calling nsadbGroupNew(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * + * Returns: + */ + +NSAPI_PUBLIC int nsadbCreateGroup(NSErr_t * errp, void * authdb, GroupObj_t * goptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + /* Open the group database for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Add this group to the database */ + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + punt: + return rv; +} + +/* + * Description (nsadbCreateUser) + * + * This function creates a new user in a specified authentication + * database. The user is described by a user object. A user + * object can be created by calling nsadbUserNew(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uoptr - pointer to user object + * + * Returns: + */ + +NSAPI_PUBLIC int nsadbCreateUser(NSErr_t * errp, void * authdb, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + /* Open the user database for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Add this user to the database */ + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbEnumerateUsers) + * + * This function is called to enumerate all of the users in a + * given authentication database to a call-back function specified + * by the caller. The call-back function is provided with a + * handle for the authentication database, an opaque value provided + * by the caller, and a pointer to a user object. See the + * description of nsadbEnumUsersHelp above for the interpretation + * of the call-back function's return value. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * argp - opaque value for call-back function + * func - pointer to call-back function + * + * Returns: + * + * If the call-back function returns a negative error code, this + * value is returned. A negative value may also be returned if + * nsadb encounters an error. Otherwise the result is the number + * of users enumerated. + */ + +NSAPI_PUBLIC int nsadbEnumerateUsers(NSErr_t * errp, void * authdb, void * argp, +#ifdef UnixWare + ArgFn_EnumUsers func) /* for ANSI C++ standard, see nsamgmt.h */ +#else + int (*func)(NSErr_t * ferrp, void * authdb, void * parg, UserObj_t * uoptr)) +#endif +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + EnumUserArgs_t args; /* arguments for enumeration helper */ + int rv; /* result value */ + + /* Open the users subdatabase for read access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + + args.authdb = authdb; + args.func = func; + args.user = argp; + args.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_userdb, + NDBF_ENUMNORM, (void *)&args, nsadbEnumUsersHelp); + if (rv < 0) goto punt; + + rv = args.rv; + + punt: + return rv; +} + +/* + * Description (nsadbEnumerateGroups) + * + * This function is called to enumerate all of the groups in a + * given authentication database to a call-back function specified + * by the caller. The call-back function is provided with a + * handle for the authentication database, an opaque value provided + * by the caller, and a pointer to a group object. See the + * description of nsadbEnumGroupsHelp above for the interpretation + * of the call-back function's return value. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * argp - opaque value for call-back function + * func - pointer to call-back function + * + * Returns: + * + * If the call-back function returns a negative error code, this + * value is returned. A negative value may also be returned if + * nsadb encounters an error. Otherwise the result is the number + * of groups enumerated. + */ + +NSAPI_PUBLIC int nsadbEnumerateGroups(NSErr_t * errp, void * authdb, void * argp, +#ifdef UnixWare + ArgFn_EnumGroups func) /* for ANSI C++ standard, see nsamgmt.h */ +#else + int (*func)(NSErr_t * ferrp, void * authdb, void * parg, GroupObj_t * goptr)) +#endif +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + EnumGroupArgs_t args; + int rv; /* result value */ + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + args.authdb = authdb; + args.func = func; + args.user = argp; + args.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_groupdb, + NDBF_ENUMNORM, (void *)&args, nsadbEnumGroupsHelp); + if (rv < 0) goto punt; + + rv = args.rv; + + punt: + return rv; +} + +/* + * Description (nsadbIsUserInGroup) + * + * This function tests whether a given user id is a member of the + * group associated with a specified group id. The caller may + * provide a list of group ids for groups to which the user is + * already known to belong, and this may speed up the check. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uid - user id + * gid - group id + * ngroups - number of group ids in grplist + * grplist - groups the user is known to belong to + * + * Returns: + * + * The return value is +1 if the user is found to belong to the + * indicated group, or 0 if the user does not belong to the group. + * An error is indicated by a negative return value. + */ + +NSAPI_PUBLIC int nsadbIsUserInGroup(NSErr_t * errp, void * authdb, + USI_t uid, USI_t gid, int ngroups, USI_t * grplist) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t dglist; /* descendant group list */ + GroupObj_t * goptr = 0; /* group object pointer */ + USI_t * idlist; /* pointer to array of group ids */ + USI_t tgid; /* test group id */ + int i; /* loop index */ + int rv; /* result value */ + + UILINIT(&dglist); + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + for (tgid = gid;;) { + + /* Get a group object for this group id */ + goptr = groupFindByGid(errp, adb->adb_groupdb, tgid); + if (goptr == 0) { + /* Error - group id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + + /* Is the user a direct member of this group? */ + if (usiPresent(&goptr->go_users, uid)) goto is_member; + + /* + * Is there any group to which the user is already known to + * belong that is a direct group member of this group? If so, + * the user is also a member of this group. + */ + + /* Scan list of groups to which the user is known to belong */ + for (i = 0; i < ngroups; ++i) { + + if (usiPresent(&goptr->go_groups, grplist[i])) goto is_member; + } + + /* Merge group member list of this group with descendants list */ + rv = uilMerge(&dglist, &goptr->go_groups); + if (rv < 0) goto punt; + + /* + * If descendants list is empty, the user is not contained in + * the specified group. + */ + if (UILCOUNT(&dglist) <= 0) { + rv = 0; + goto punt; + } + + /* Remove the next id from the descendants list */ + idlist = UILLIST(&dglist); + tgid = idlist[0]; + + rv = usiRemove(&dglist, tgid); + if (rv < 0) goto punt; + + groupFree(goptr); + goptr = 0; + } + + is_member: + rv = 1; + + punt: + if (goptr) { + groupFree(goptr); + } + UILFREE(&dglist); + return rv; +} + +/* + * Description (nsadbModifyGroup) + * + * This function is called to write modifications to a group to + * a specified authentication database. The group is assumed to + * already exist in the database. Information about the group + * is passed in a group object. This function should not be used + * to alter the lists of group members or parents. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to modified group object + * + * Returns: + * + * The return value is zero if the group information is successfully + * updated. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbModifyGroup(NSErr_t * errp, void * authdb, GroupObj_t * goptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + punt: + return rv; +} + +/* + * Description (nsadbModifyUser) + * + * This function is called to write modifications to a user to + * a specified authentication database. The user is assumed to + * already exist in the database. Information about the user + * is passed in a user object. This function should not be used + * to modify the list of groups which contain the user. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uoptr - pointer to modified user object + * + * Returns: + * + * The return value is zero if the user information is successfully + * updated. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbModifyUser(NSErr_t * errp, void * authdb, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbRemoveGroup) + * + * This function is called to remove a given group name from + * a specified authentication database. This can cause updates + * to both the user and group subdatabases. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * name - pointer to name of group to remove + * + * Returns: + * + * The return value is zero if the group information is successfully + * removed. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbRemoveGroup(NSErr_t * errp, void * authdb, char * name) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + UserObj_t * uoptr = 0; /* user object pointer */ + GroupObj_t * goptr = 0; /* group object pointer */ + GroupObj_t * ogoptr = 0; /* other group object pointer */ + char * ugname; /* user or group name */ + USI_t * list; /* pointer into user/group id list */ + int cnt; /* count of user or group ids */ + int i; /* loop index */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Open the groups subdatabase for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Look up the group to be removed, and get a group object */ + rv = nsadbFindByName(errp, authdb, name, AIF_GROUP, (void **)&goptr); + if (rv != AIF_GROUP) { + if (rv < 0) goto punt; + goto err_nogroup; + } + + /* Mark the group for delete */ + goptr->go_flags |= GOF_DELPEND; + + /* Does the specified group belong to any groups? */ + cnt = UILCOUNT(&goptr->go_pgroups); + if (cnt > 0) { + + /* Yes, for each parent group ... */ + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemGroupFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_pgroups); + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &ugname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_GROUP, (void **)&ogoptr); + if (rv < 0) goto punt; + + /* Remove the specified group from the parent group */ + rv = nsadbRemGroupFromGroup(errp, authdb, ogoptr, goptr); + if (rv < 0) goto punt; + + /* Free the parent group object */ + groupFree(ogoptr); + ogoptr = 0; + } + } + + /* Are there any group members of this group? */ + cnt = UILCOUNT(&goptr->go_groups); + if (cnt > 0) { + + /* For each group member of the group ... */ + + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemGroupFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_groups); + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &ugname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_GROUP, (void **)&ogoptr); + if (rv < 0) goto punt; + + /* Remove member group from the specified group */ + rv = nsadbRemGroupFromGroup(errp, authdb, goptr, ogoptr); + if (rv < 0) goto punt; + + /* Free the member group object */ + groupFree(ogoptr); + ogoptr = 0; + } + } + + /* Are there any direct user members of this group? */ + cnt = UILCOUNT(&goptr->go_users); + if (cnt > 0) { + + /* Yes, open users subdatabase for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* For each user member of the group ... */ + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemUserFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_users); + + /* Get user name associated with the user id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_USER, &ugname); + if (rv < 0) goto punt; + + /* Look up the user by name and get a user object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_USER, (void **)&uoptr); + if (rv < 0) goto punt; + + /* Remove user from the group */ + rv = nsadbRemUserFromGroup(errp, authdb, goptr, uoptr); + if (rv < 0) goto punt; + + /* Free the member user object */ + userFree(uoptr); + uoptr = 0; + } + } + + /* Free the group object for the specified group */ + groupFree(goptr); + goptr = 0; + + /* Now we can remove the group entry */ + rv = groupRemove(errp, adb->adb_groupdb, 0, (NTS_t)name); + + return rv; + + err_nogroup: + eid = NSAUERR4100; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 2, adb->adb_dbname, name); + goto punt; + + punt: + /* Free any user or group objects that we created */ + if (ogoptr != 0) { + groupFree(ogoptr); + } + if (uoptr != 0) { + userFree(uoptr); + } + if (goptr != 0) { + groupFree(goptr); + } + return rv; +} + +/* + * Description (nsadbRemoveUser) + * + * This function is called to remove a given user name from + * a specified authentication database. This can cause updates + * to both the user and user subdatabases. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * name - pointer to name of user to remove + * + * Returns: + * + * The return value is zero if the user information is successfully + * removed. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbRemoveUser(NSErr_t * errp, void * authdb, char * name) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + UserObj_t * uoptr = 0; /* user object pointer */ + GroupObj_t * goptr = 0; /* group object pointer */ + char * gname; /* group name */ + USI_t * list; /* pointer into group id list */ + int gcnt; /* number of groups containing user */ + int i; /* loop index */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Open the users subdatabase for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Look up the user to be removed, and get a user object */ + rv = nsadbFindByName(errp, authdb, name, AIF_USER, (void **)&uoptr); + if (rv != AIF_USER) { + if (rv < 0) goto punt; + goto err_nouser; + } + + /* Mark the user for delete */ + uoptr->uo_flags |= UOF_DELPEND; + + /* Does this user belong to any groups? */ + gcnt = UILCOUNT(&uoptr->uo_groups); + if (gcnt > 0) { + + /* Yes, get pointer to list of group ids */ + list = UILLIST(&uoptr->uo_groups); + + /* Open groups subdatabase for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* For each group that the user belongs to ... */ + for (i = 0; i < gcnt; ++i) { + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &gname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, gname, AIF_GROUP, (void **)&goptr); + if (rv < 0) goto punt; + + /* Remove user from group if it's a direct member */ + rv = nsadbRemUserFromGroup(errp, authdb, goptr, uoptr); + if (rv < 0) goto punt; + + /* Free the group object */ + groupFree(goptr); + goptr = 0; + + ++list; + } + } + +#ifdef CLIENT_AUTH + /* Remove certificate mapping for user, if any */ + rv = nsadbRemoveUserCert(errp, authdb, name); +#endif + + /* Free the user object */ + userFree(uoptr); + + /* Now we can remove the user entry */ + rv = userRemove(errp, adb->adb_userdb, 0, (NTS_t)name); + + return rv; + + err_nouser: + eid = NSAUERR4000; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 2, adb->adb_dbname, name); + goto punt; + + punt: + if (goptr != 0) { + groupFree(goptr); + } + if (uoptr != 0) { + userFree(uoptr); + } + return rv; +} + +/* + * Description (nsadbRemGroupFromGroup) + * + * This function removes a given group C from a parent group P. + * The group C must be a direct member of the group P. However, + * group C may also be a member of one or more of P's ancestor or + * descendant groups, and this function deals with that. The + * group entries for C and P are updated in the group database. + * But the real work is updating the groups lists of all of the + * users contained in C. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * pgoptr - pointer to parent group object + * cgoptr - pointer to child group object + * + * Returns: + * + * The return value is zero if group C was a direct member of + * group P, and was removed successfully. A return value of +1 + * indicates that group C was not a direct member of the group P. + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbRemGroupFromGroup(NSErr_t * errp, void * authdb, + GroupObj_t * pgoptr, GroupObj_t * cgoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t dglist; /* list of descendant groups of C */ + GroupObj_t * dgoptr; /* descendant group object pointer */ + UserObj_t * uoptr; /* user object pointer */ + USI_t * gidlist; /* pointer to group id array */ + USI_t * userlist; /* pointer to array of descendant user ids */ + USI_t dgid; /* descendant group id */ + int iusr; /* index on descendant user list */ + int usercnt; /* count of descendant users */ + int igrp; /* index of group in user group id list */ + int rv; /* result value */ + + dgoptr = 0; + uoptr = 0; + + /* Initialize a list of descendant groups of C */ + UILINIT(&dglist); + + /* Is group C a direct member of group P? */ + if (!usiPresent(&pgoptr->go_groups, cgoptr->go_gid)) { + + /* No, nothing to do */ + return 1; + } + + /* Remove group C from group P's group member list */ + rv = usiRemove(&pgoptr->go_groups, cgoptr->go_gid); + if (rv < 0) goto punt; + + /* Remove group P from group C's parent group list */ + rv = usiRemove(&cgoptr->go_pgroups, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Write out the updated group C object */ + cgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, cgoptr); + if (rv) goto punt; + + /* Write out the updated group P object */ + pgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, pgoptr); + if (rv) goto punt; + + /* Now check the group lists of all users contained in group C */ + dgoptr = cgoptr; + dgid = cgoptr->go_gid; + + for (;;) { + + /* Scan the direct user members of this descendant group */ + usercnt = UILCOUNT(&dgoptr->go_users); + userlist = UILLIST(&dgoptr->go_users); + + for (iusr = 0; iusr < usercnt; ++iusr) { + + /* Get a user object for this user member */ + uoptr = userFindByUid(errp, adb->adb_userdb, userlist[iusr]); + if (uoptr == 0) { + /* Error - user id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + + /* Scan the group list for this user */ + for (igrp = 0; igrp < UILCOUNT(&uoptr->uo_groups); ) { + + gidlist = UILLIST(&uoptr->uo_groups); + + /* Is the user a member of this group? */ + if (nsadbIsUserInGroup(errp, authdb, + uoptr->uo_uid, gidlist[igrp], + igrp, gidlist)) { + + /* Yes, step to next group id */ + ++igrp; + } + else { + /* + * No, remove it from the user's list of groups. The + * next group id to consider will be shifted into the + * igrp position when the current id is removed. + */ + rv = usiRemove(&uoptr->uo_groups, gidlist[igrp]); + if (rv < 0) goto punt; + uoptr->uo_flags |= UOF_MODIFIED; + } + } + + /* Write out the user object if it was changed */ + if (uoptr->uo_flags & UOF_MODIFIED) { + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv < 0) goto punt; + } + + /* Free the user object */ + userFree(uoptr); + uoptr = 0; + } + + /* + * Merge the direct member groups of this group into the + * descendants list. + */ + rv = uilMerge(&dglist, &dgoptr->go_groups); + if (rv < 0) goto punt; + + /* Free this descendant group object */ + if (dgoptr != cgoptr) { + groupFree(dgoptr); + dgoptr = 0; + } + + /* If the descendants list is empty, we're done */ + if (UILCOUNT(&dglist) <= 0) break; + + /* Remove the next group id from the descendants list */ + gidlist = UILLIST(&dglist); + dgid = gidlist[0]; + rv = usiRemove(&dglist, dgid); + if (rv < 0) goto punt; + + /* Get a group object for this descendant group */ + dgoptr = groupFindByGid(errp, adb->adb_groupdb, dgid); + if (dgoptr == 0) { + /* Error - group id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + } + + UILFREE(&dglist); + return 0; + + punt: + if (dgoptr) { + groupFree(dgoptr); + } + if (uoptr) { + userFree(uoptr); + } + UILFREE(&dglist); + return rv; +} + +/* + * Description (nsadbRemUserFromGroup) + * + * This function removes a given user from a specified group G. + * The user must be a direct member of the group. However, the + * user may also be a member of one or more of G's descendant + * groups, and this function deals with that. The group entry + * for G is updated in the group database, with the user removed + * from its user member list. The user entry is updated in the + * user database, with an updated list of all groups which now + * contain the user. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * uoptr - pointer to user object + * + * Returns: + * + * The return value is zero if the user was a direct member of the + * group, and was removed successfully. A return value of +1 + * indicates that the user was not a direct member of the + * group. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbRemUserFromGroup(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USI_t * idlist; /* pointer to user group id array */ + USI_t tgid; /* test group id */ + int igrp; /* position in user group list */ + int rv; /* result value */ + + /* Is the user a direct member of the group? */ + if (!usiPresent(&goptr->go_users, uoptr->uo_uid)) { + + /* No, nothing to do */ + return 1; + } + + /* Remove the user from the group's user member list */ + rv = usiRemove(&goptr->go_users, uoptr->uo_uid); + if (rv < 0) goto punt; + + /* If the user object is pending deletion, no need to open databases */ + if (!(uoptr->uo_flags & UOF_DELPEND)) { + + /* + * Open user database since the group list of the user + * will be modified. + */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + } + + /* + * Write out the updated group object. This must be done here + * because nsadbIsUserInGroup() in the loop below will read the + * entry for this group, and it needs to reflect the user's + * removal from being a direct member of the group. This does + * not preclude the possibility that the user will still be an + * indirect member of this group. + */ + goptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + if (rv) goto punt; + + /* If a delete is pending on the user, we're done */ + if (uoptr->uo_flags & UOF_DELPEND) goto punt; + + /* + * Begin loop to check whether user is still a member of each + * of the groups in its group list. Note that the group list + * may shrink during an iteration of the loop. + */ + + for (igrp = 0; igrp < UILCOUNT(&uoptr->uo_groups); ) { + + /* Get pointer to the user's array of group ids */ + idlist = UILLIST(&uoptr->uo_groups); + + /* Get the group id of the next group to consider */ + tgid = idlist[igrp]; + + /* Is the user a member of this group? */ + if (nsadbIsUserInGroup(errp, authdb, + uoptr->uo_uid, tgid, igrp, idlist)) { + + /* Yes, step to next group id */ + ++igrp; + } + else { + + /* + * No, remove it from the user's list of groups. The + * next group id to consider will be shifted into the + * igrp position when the current id is removed. + */ + rv = usiRemove(&uoptr->uo_groups, tgid); + if (rv < 0) goto punt; + } + } + + /* Write out the updated user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbSuperGroups) + * + * This function builds a list of the group ids for all groups + * which contain, directly or indirectly, a specified group as + * a subgroup. We call these the supergroups of the specified + * group. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * gsuper - pointer to list to contain supergroups + * (caller must initialize) + * + * Returns: + * + * Returns the number of elements in gsuper if successful. An + * error is indicated by a negative return value. + */ + +NSAPI_PUBLIC int nsadbSuperGroups(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, USIList_t * gsuper) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t aglist; /* ancestor group id list */ + GroupObj_t * aoptr; /* ancestor group object pointer */ + USI_t * idlist; /* pointer to array of group ids */ + USI_t id; /* current group id */ + int rv; /* result value */ + + /* Initialize an empty ancestor group list */ + UILINIT(&aglist); + + /* Enter loop with specified group as first ancestor */ + id = goptr->go_gid; + aoptr = goptr; + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + /* Loop until the ancestor list is empty */ + for (;;) { + + /* Merge parent groups of current ancestor into ancestor list */ + rv = uilMerge(&aglist, &aoptr->go_pgroups); + if (rv < 0) goto punt; + + /* Also merge parent groups into the result list */ + rv = uilMerge(gsuper, &aoptr->go_pgroups); + if (rv < 0) goto punt; + + /* Free the ancestor group object (but not the original) */ + if (aoptr != goptr) { + groupFree(aoptr); + aoptr = 0; + } + + /* Exit the loop if the ancestor list is empty */ + if (UILCOUNT(&aglist) <= 0) break; + + /* Get pointer to array of ancestor group ids */ + idlist = UILLIST(&aglist); + + /* Remove the first ancestor */ + id = idlist[0]; + rv = usiRemove(&aglist, id); + + /* Get a group object for the ancestor */ + aoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (aoptr == 0) { + /* Error - group not found, database inconsistent */ + rv = -1; + goto punt; + } + } + + return UILCOUNT(gsuper); + + punt: + /* Handle error */ + + /* Free ancestor list */ + UILFREE(&aglist); + + return rv; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/nsautherr.cpp b/lib/libaccess/nsautherr.cpp new file mode 100644 index 00000000..308c0698 --- /dev/null +++ b/lib/libaccess/nsautherr.cpp @@ -0,0 +1,126 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsautherr.c) + * + * This module provides facilities for handling authentication + * errors. + */ + +#include <string.h> +#include "base/systems.h" +#include "prprf.h" +#include "libaccess/nserror.h" +#include "libaccess/nsautherr.h" + +/* Error message formats XXX internationalize XXX */ +static char * nsaerrnomem = "insufficient dynamic memory"; +static char * nsaerrinval = "invalid argument"; +static char * nsaerropen = "error opening %s"; +static char * nsaerrmkdir = "error creating %s"; +static char * nsaerrname = "%s not found in database %s"; +static char * unknownerr = "error code %d"; + +/* + * Description (nsadbErrorFmt) + * + * This function formats an authentication error message into a + * buffer provided by the caller. The authentication error + * information is passed in an error list structure. The caller + * can indicate how many error frames should be processed. A + * newline is inserted between messages for different error frames. + * + * Arguments: + * + * errp - error frame list pointer + * msgbuf - pointer to error message buffer + * maxlen - maximum length of generated message + * maxdepth - maximum depth for traceback + */ + +NSAPI_PUBLIC void nsadbErrorFmt(NSErr_t * errp, char * msgbuf, int maxlen, int maxdepth) +{ + NSEFrame_t * efp; /* error frame pointer */ + int len; /* length of error message text */ + int depth = 0; /* current depth */ + + msgbuf[0] = 0; + + for (efp = errp->err_first; efp != 0; efp = efp->ef_next) { + + /* Stop if the message buffer is full */ + if (maxlen <= 0) break; + + if (depth > 0) { + /* Put a newline between error frame messages */ + *msgbuf++ = '\n'; + if (--maxlen <= 0) break; + } + + /* Identify the facility generating the error and the id number */ + len = PR_snprintf(msgbuf, maxlen, + "[%s%d] ", efp->ef_program, efp->ef_errorid); + msgbuf += len; + maxlen -= len; + + if (maxlen <= 0) break; + + len = 0; + + if (!strcmp(efp->ef_program, NSAuth_Program)) { + + switch (efp->ef_retcode) { + case NSAERRNOMEM: + strncpy(msgbuf, nsaerrnomem, maxlen); + len = strlen(nsaerrnomem); + break; + + case NSAERRINVAL: + /* Invalid function argument error: */ + strncpy(msgbuf, nsaerrinval, maxlen); + len = strlen(nsaerrinval); + break; + + case NSAERROPEN: + /* File open error: filename */ + if (efp->ef_errc == 1) { + len = PR_snprintf(msgbuf, maxlen, nsaerropen, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case NSAERRMKDIR: + /* error creating database directory: database name */ + if (efp->ef_errc == 1) { + len = PR_snprintf(msgbuf, maxlen, nsaerrmkdir, + efp->ef_errv[0]); + } + break; + + case NSAERRNAME: + /* user or group name not found: database, name */ + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, nsaerrname, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + default: + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + break; + } + } + else { + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + } + + msgbuf += len; + maxlen -= len; + + if (++depth >= maxdepth) break; + } +} diff --git a/lib/libaccess/nscert.cpp b/lib/libaccess/nscert.cpp new file mode 100644 index 00000000..97939b24 --- /dev/null +++ b/lib/libaccess/nscert.cpp @@ -0,0 +1,963 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Description (nsadb.c) + * + * This module contains routines for accessing and storing information + * in a Netscape client certificate to username database. This + * database is used to associate a username with a client certificate + * that is presented to a server. + */ + +#if defined(CLIENT_AUTH) + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#include <base/fsmutex.h> +#include <libaccess/nsdbmgmt.h> +#define __PRIVATE_NSADB +#include <libaccess/nsadb.h> +#include <libaccess/nsamgmt.h> + +static FSMUTEX nscert_lock = 0; + +NSAPI_PUBLIC int nsadbCertInitialize(void) +{ +#ifdef XP_UNIX + nscert_lock = fsmutex_init("NSCERTMAP", geteuid(), + FSMUTEX_VISIBLE|FSMUTEX_NEEDCRIT); +#else /* XP_WIN32 */ + char winuser[128]; + DWORD wulength; + strcpy(winuser, "NSCERTMAP_"); + wulength = 128 - 11; + GetUserName(winuser+10, &wulength); + nscert_lock = fsmutex_init(winuser, 0, + FSMUTEX_VISIBLE|FSMUTEX_NEEDCRIT); +#endif + return (nscert_lock == 0) ? -1 : 0; +} + +NSAPI_PUBLIC int nsadbDecodeCertRec(int reclen, char * recptr, + CertObj_t * coptr) +{ + ATR_t cp = (ATR_t)recptr; /* current pointer into record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + + /* Parse user DB record */ + while ((cp - (ATR_t)recptr) < reclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case CAT_USERNAME: /* username associated with cert */ + cp = NTSDECODE(cp, (NTS_t *)&coptr->co_username); + break; + + case CAT_CERTID: /* certificate-to-user map id */ + cp = USIDECODE(cp, &coptr->co_certid); + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + + return 0; +} + +/* + * Description (nsadbDecodeCertKey) + * + * This function decodes information from a certificate key. + * Currently a certificate key includes the DER encoding of the + * issuer and subject distinguished names. This is used to + * uniquely identify client certificates, even across certificate + * renewals. SECItems for the issuer and subject are provided + * by the caller. These are updated with the pointers and lengths + * of DER encodings, which can be decoded using nsadbDecodeCertName() + * into SECName structures. The returned SECItems refer to data + * in the provided key buffer. + * + * Arguments: + * + * keylen - length of the certificate key encoding + * keyptr - buffer containing certificate key encoding + * issuer - pointer to SECItem for returning issuer + * subject - pointer to SECItem for returning subject + * + * Returns: + * + * Zero is returned if no errors are encountered. Otherwise -1. + */ + +NSAPI_PUBLIC int nsadbDecodeCertKey(int keylen, char * keyptr, + SECItem * issuer, SECItem * subject) +{ + ATR_t cp = (ATR_t)keyptr; /* current pointer into DB record */ + USI_t len; /* attribute value encoding length */ + USI_t tag; /* attribute tag */ + + /* Parse user DB record */ + while ((cp - (ATR_t)keyptr) < keylen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case KAT_ISSUER: /* issuer DER encoding */ + issuer->len = len; + issuer->data = cp; + cp += len; + break; + + case KAT_SUBJECT: /* subject name DER encoding */ + subject->len = len; + subject->data = cp; + cp += len; + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + + return 0; +} + +/* + * Description (nsadbEncodeCertKey) + * + * This function encodes information provided by the caller into + * a certificate key. The certificate key is returned in a + * buffer obtained from MALLOC(). + * + * Arguments: + * + * issuer - pointer to SECItem for issuer DER + * subject - pointer to SECItem for subject DER + * keylen - returned length of certificate key + * keyptr - returned pointer to buffer containing + * certificate key encoding + * + * Returns: + * + * Zero is returned if no errors are encountered. Otherwise -1. + */ + +NSAPI_PUBLIC int nsadbEncodeCertKey(SECItem * issuer, SECItem * subject, + int * keylen, char **keyptr) +{ + ATR_t cp; /* pointer into key buffer */ + ATR_t kptr; /* pointer to key buffer */ + int klen; /* length of key */ + int rv = -1; + + /* Compute length of key encoding */ + klen = 1 + USILENGTH(issuer->len) + issuer->len + + 1 + USILENGTH(subject->len) + subject->len; + + /* Allocate buffer to contain the key */ + kptr = (ATR_t)MALLOC(klen); + if (kptr) { + /* Encode issuer and subject as attributes */ + cp = kptr; + *cp++ = KAT_ISSUER; + cp = USIENCODE(cp, issuer->len); + memcpy(cp, issuer->data, issuer->len); + cp += issuer->len; + *cp++ = KAT_SUBJECT; + cp = USIENCODE(cp, subject->len); + memcpy(cp, subject->data, subject->len); + rv = 0; + } + + /* Return length and buffer pointer */ + if (keylen) *keylen = klen; + *keyptr = (char *)kptr; + + return rv; +} + +/* + * Description (nsadbEnumCertsHelp) + * + * This is a local function that is called by NSDB during certificate + * to user database enumeration. It decodes certificate records into + * CertObj_t structures, and presents them to the caller of + * nsadbEnumerateCerts(), via the specified call-back function. + * The call-back function return value may be a negative error code, + * which will cause enumeration to stop, and the error code will be + * returned from nsadbEnumerateCerts(). If the return value of the + * call-back function is not negative, it can contain one or more of + * the following flags: + * + * ADBF_KEEPOBJ - do not free the CertObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to CertEnumArgs_t structure + * keylen - certificate record key length + * keyptr - certificate record key + * reclen - length of certificate record + * recptr - pointer to certificate record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct CertEnumArgs_s CertEnumArgs_t; +struct CertEnumArgs_s { + int rv; /* just a return value */ + void * client; /* the current key for lookup */ + void * authdb; /* the authentication data base */ + CertEnumCallback func; /* client's callback function */ +}; + +static int nsadbEnumCertsHelp(NSErr_t * errp, void * parg, + int keylen, char * keyptr, + int reclen, char * recptr) +{ + CertEnumArgs_t * ce = (CertEnumArgs_t *)parg; + CertObj_t * coptr; + int rv = NSAERRNOMEM; + + /* Allocate a CertObj_t structure and initialize it */ + coptr = (CertObj_t *)MALLOC(sizeof(CertObj_t)); + if (coptr) { + + coptr->co_issuer.data = 0; + coptr->co_subject.data = 0; + coptr->co_username = 0; + coptr->co_certid = 0; + + /* Decode the certificate key */ + rv = nsadbDecodeCertKey(keylen, keyptr, + &coptr->co_issuer, &coptr->co_subject); + + /* Decode the certificate record */ + rv = nsadbDecodeCertRec(reclen, recptr, coptr); + + /* Pass the CertObj_t to the callback function */ + rv = (*ce->func)(errp, ce->authdb, ce->client, coptr); + if (rv >= 0) { + + /* Count the number of records seen */ + ce->rv += 1; + + /* Free the user object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + nsadbFreeCertObj(coptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* return the error code */ + ce->rv = rv; + } + } + + return rv; +} + +/* + * Description (nsadbEnumerateClients) + * + * (See description for nsadbEnumerateUsers) + */ + +NSAPI_PUBLIC int nsadbEnumerateCerts(NSErr_t * errp, void * authdb, + void * argp, CertEnumCallback func) +{ + AuthDB_t * adb = (AuthDB_t*)authdb; + CertEnumArgs_t helper_data; + int rv; + + /* Open the certificate subdatabase for read access */ + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv >= 0) { + helper_data.authdb = authdb; + helper_data.func = func; + helper_data.client = argp; + helper_data.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_certdb, NDBF_ENUMNORM, + (void*)&helper_data, nsadbEnumCertsHelp); + } + + return (rv < 0) ? rv: helper_data.rv; +} + +NSAPI_PUBLIC void nsadbFreeCertObj(CertObj_t * coptr) +{ + if (coptr) { + FREE(coptr->co_username); + FREE(coptr); + } +} + +NSAPI_PUBLIC int nsadbGetCertById(NSErr_t * errp, void * authdb, + USI_t certid, CertObj_t **coptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + CertObj_t * cop = 0; + char * keyptr; + char * recptr; + int keylen; + int reclen; + int rv; + + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + + /* Get the name corresponding to the id */ + rv = ndbIdToName(errp, adb->adb_certdb, certid, &keylen, &keyptr); + if (rv < 0) goto punt; + + rv = ndbFindName(errp, adb->adb_certdb, + keylen, keyptr, &reclen, &recptr); + if (rv < 0) goto punt; + + /* Allocate a CertObj_t structure and initialize it */ + cop = (CertObj_t *)MALLOC(sizeof(CertObj_t)); + if (cop) { + + cop->co_issuer.data = 0; + cop->co_subject.data = 0; + cop->co_username = 0; + cop->co_certid = 0; + + /* Decode the certificate key */ + rv = nsadbDecodeCertKey(keylen, keyptr, + &cop->co_issuer, &cop->co_subject); + + /* Decode the certificate record */ + rv = nsadbDecodeCertRec(reclen, recptr, cop); + + } + + punt: + if (coptr) *coptr = cop; + return rv; +} + +/* + * Description (nsadbGetUserByCert) + * + * This function looks up a specified client certificate in the + * authentication database. It returns a pointer to the username + * associated with the client certificate, if any. The username + * buffer remains valid until the authentication database is + * closed. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * cert - pointer to client certificate + * username - pointer to returned user name (or null) + * + * Returns: + * + * The return value will be zero if the certificate is found. Also, + * *username will be set to the string value of the associated username + * iff username is not null. + */ + +NSAPI_PUBLIC int nsadbGetUserByCert(NSErr_t * errp, void * authdb, + CERTCertificate * cert, char **username) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t cp; /* current pointer into DB record */ + char * user = 0; /* pointer to username */ + char * keyptr = 0; /* pointer to cert key */ + char * recptr; /* pointer to cert db record */ + int keylen; /* length of cert key */ + int reclen; /* length of cert db record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + int rv; + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&cert->derIssuer, &cert->derSubject, + &keylen, &keyptr); + + if (adb->adb_certdb == NULL) { + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + } + + rv = ndbFindName(errp, adb->adb_certdb, + keylen, keyptr, &reclen, &recptr); + if (rv < 0) goto punt; + + /* Parse cert DB record */ + cp = (ATR_t)recptr; + + while ((cp - (ATR_t)recptr) < reclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* We want the CAT_USERNAME attribute */ + if (tag == CAT_USERNAME) { + + /* Get the username associated with the cert */ + user = (char *)cp; + break; + } + + /* Just skip other attributes */ + cp += len; + } + + punt: + if (keyptr) { + FREE(keyptr); + } + if (username) *username = user; + return rv; +} + +/* + * Description (see description for nsadbOpenUsers) + */ + +int nsadbOpenCerts(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t *adb = (AuthDB_t*)authdb; + char *dbname = NULL; /* user database name */ + int dblen; /* strlen(adb_dbname) */ + int version; /* database version */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == NULL) goto err_inval; + + /* Is the user database already open? */ + if (adb->adb_certdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + /* Allocate space for the user database filename */ + dblen = strlen(adb->adb_dbname); + + dbname = (char *)MALLOC(dblen + strlen(ADBCERTDBNAME) + 2); + if (dbname == 0) goto err_nomem; + + /* Construct user database name */ + strcpy(dbname, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (dbname[dblen-1] != FILE_PATHSEP) { + dbname[dblen] = FILE_PATHSEP; + dbname[dblen+1] = 0; + ++dblen; + } + + strcpy(&dbname[dblen], ADBCERTDBNAME); + + if (nscert_lock == 0) { + rv = nsadbCertInitialize(); + if (rv < 0) goto err_lock; + } + adb->adb_certlock = nscert_lock; + if (adb->adb_certlock == 0) goto punt; + + fsmutex_lock((FSMUTEX)(adb->adb_certlock)); + + adb->adb_certdb = ndbOpen(errp, + dbname, 0, NDB_TYPE_CLIENTDB, &version); + if (adb->adb_certdb == 0) { + fsmutex_unlock((FSMUTEX)(adb->adb_certlock)); + goto err_open; + } + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_CREAD|ADBF_CWRITE); + if (flags & ADBF_CWRITE) adb->adb_flags |= ADBF_CWRITE; + else adb->adb_flags |= ADBF_CREAD; + rv = 0; + + punt: + if (dbname != NULL) FREE(dbname); + return rv; + + err_inval: + eid = NSAUERR3400; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3420; + rv = NSAERRNOMEM; + goto err_ret; + + err_lock: + eid = NSAUERR3430; + rv = NSAERRLOCK; + goto err_ret; + + err_open: + eid = NSAUERR3440; + rv = NSAERROPEN; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, dbname); + goto punt; + +} + +NSAPI_PUBLIC void nsadbCloseCerts(void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + + if (adb->adb_certnm != 0) { + /* Close the username-to-certid database */ + nsadbCloseCertUsers(authdb, flags); + } + + if (adb->adb_certdb != 0) { + + ndbClose(adb->adb_certdb, 0); + adb->adb_certdb = 0; + + /* + * A lock is held for the certificate map DB as long as it is + * open, so release the lock now. + */ + fsmutex_unlock((FSMUTEX)(adb->adb_certlock)); + } +} + +/* + * Description (nsadbOpenCertUsers) + * + * This function opens a database that maps user names to client + * certificates. The database appears as "Certs.nm" in the + * authentication database directory. This function requires + * that the primary certificate database be opened (Certs.db) + * first, and will open it if necessary, acquiring a global + * lock in the process. The lock will not be released until + * nsadbCloseCerts() or nsadbClose() is called. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - same as nsadbOpenCerts() + * + * Returns: + * + * The return value is zero if the operation is successful. + * Otherwise a negative error code is returned. + */ + +NSAPI_PUBLIC int nsadbOpenCertUsers(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * dbname = 0; + int dblen; + int oflags = O_RDONLY; /* assume read-only access */ + int eid; + int rv; + + /* The primary certificate mapping database must be open first */ + if (adb->adb_certdb != 0) { + + /* It's open, but is it read-only when we need write? */ + if (((flags & adb->adb_flags) == 0) && (flags & ADBF_CWRITE)) { + + /* Yes, close it */ + nsadbCloseCerts(authdb, 0); + } + } + + /* Open it for the desired access if necessary */ + if (adb->adb_certdb == 0) { + /* + * Open it for the desired access. Note that this acquires + * a global lock which is not released until nsadbClose() is + * called for the entire authentication database. + */ + rv = nsadbOpenCerts(errp, authdb, flags); + if (rv < 0) { + /* Go no further if that failed */ + return rv; + } + } + + /* Now look at the username-to-certid database in particular */ + if (adb->adb_certnm && (adb->adb_flags & flags)) { + + /* The database is already open for the desired access */ + return 0; + } + + dblen = strlen(adb->adb_dbname); + dbname = (char *)MALLOC(dblen + strlen(ADBUMAPDBNAME) + 2); + strcpy(dbname, adb->adb_dbname); + if (dbname[dblen-1] != FILE_PATHSEP) { + dbname[dblen] = FILE_PATHSEP; + dbname[++dblen] = 0; + } + strcpy(&dbname[dblen], ADBUMAPDBNAME); + + /* Check for write access and set open flags appropriately if so */ + if (flags & ADBF_CWRITE) { + oflags = O_CREAT|O_RDWR; + } + + /* Open the username-to-certid database */ +// adb->adb_certnm = dbopen(dbname, oflags, 0644, DB_HASH, 0); + adb->adb_certnm = 0; + if (adb->adb_certnm == 0) goto err_open; + + punt: + FREE(dbname); + + return rv; + + err_open: + eid = NSAUERR3600; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, dbname); + goto punt; +} + +/* + * Description (nsadbFindCertUser) + * + * This function checks to see whether a client certificate is + * registered for a specified user name. If so, it returns the + * certificate mapping id (for use with nsadbGetCertById()). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * username - pointer to user name string + * id - pointer to returned certificate mapping id + * + * Returns: + * + * If a certificate is registered for the specified user, the return + * value is zero and the certificate mapping id is returned via 'id'. + * Otherwise the return value is a negative error code (nsautherr.h) + * and an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbFindCertUser(NSErr_t * errp, void * authdb, + const char * username, USI_t * id) +{ + int eid; + int rv; + eid = NSAUERR3700; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +/* + * Description (nsadbAddCertUser) + * + * This function adds an entry to the username-to-cert id database, + * with a given username and certificate mapping id. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * username - pointer to user name string + * id - certificate mapping id + * + * Returns: + * + * If the entry is added successfully, the return value is zero. + * Otherwise the return value is a negative error code (nsautherr.h) + * and an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbAddCertUser(NSErr_t * errp, void * authdb, + const char * username, USI_t id) +{ + /* Need to be ported on NSS 3.2 */ + int eid; + int rv; + + eid = NSAUERR3800; + rv = NSAERRPUT; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +NSAPI_PUBLIC int nsadbRemoveCertUser(NSErr_t * errp, void * authdb, + char * username) +{ + /* Need to be ported on NSS 3.2 */ + int eid; + int rv; + + eid = NSAUERR3800; + rv = NSAERRPUT; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +NSAPI_PUBLIC void nsadbCloseCertUsers(void * authdb, int flags) +{ + /* Need to be ported on NSS 3.2 */ +} + +/* + * Description (nsadbPutUserByCert) + * + * This function looks up a stores a client certificate mapping + * in the authentication database along with the associated username. + * It assumes that a record with the specified certificate key does + * not already exist, and will replace it if it does. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * certLen - length of the certificate key + * cert - certificate key pointer + * user - username to be associated with the + * certificate + * + * Returns: + * + */ + +NSAPI_PUBLIC int nsadbPutUserByCert(NSErr_t * errp, void * authdb, + CERTCertificate * cert, + const char * username) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t cp; /* pointer into cert record contents */ + char * keyptr = 0; /* pointer to cert record key */ + char * recptr = 0; /* pointer to cert record contents */ + int keylen; /* length of cert record key */ + int reclen; /* length of cert record contents */ + USI_t certid; + int usrlen; + int certidlen; + int eid; + int rv; + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&cert->derIssuer, &cert->derSubject, + &keylen, &keyptr); + + /* Open the username-to-cert id database for write */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CWRITE); + if (rv) goto punt; + + /* If the username is already mapped to a cert, it's an error */ + certid = 0; + rv = nsadbFindCertUser(errp, authdb, username, &certid); + if (rv == 0) goto err_map; + + /* + * Allocate a certificate id and write a record mapping this id + * to the specified certificate key. + */ + certid = 0; + rv = ndbAllocId(errp, adb->adb_certdb, keylen, keyptr, &certid); + if (rv) goto punt; + + /* Record the username as being mapped to the allocated cert id */ + rv = nsadbAddCertUser(errp, authdb, username, certid); + if (rv < 0) goto punt; + + nsadbCloseCertUsers(authdb, 0); + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* CAT_USERNAME attribute: tag, length, NTS */ + usrlen = NTSLENGTH(username); + if (usrlen > 127) goto err_user; + reclen = 2 + usrlen; + + /* CAT_CERTID attribute: tag, length, USI */ + certidlen = USILENGTH(certid); + reclen += 2 + certidlen; + + /* Allocate the attribute record buffer */ + recptr = (char *)MALLOC(reclen); + if (recptr) { + + cp = (ATR_t)recptr; + + /* Encode CAT_USERNAME attribute */ + *cp++ = CAT_USERNAME; + *cp++ = usrlen; + cp = NTSENCODE(cp, (NTS_t)username); + + /* Encode CAT_CERTID attribute */ + *cp++ = CAT_CERTID; + *cp++ = certidlen; + cp = USIENCODE(cp, certid); + } + + /* Store the record in the database under the certificate key */ + rv = ndbStoreName(errp, adb->adb_certdb, + 0, keylen, keyptr, reclen, recptr); + + punt: + if (keyptr) { + FREE(keyptr); + } + if (recptr) { + FREE(recptr); + } + + return rv; + + err_user: + eid = NSAUERR3500; + rv = NSAERRINVAL; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adb->adb_dbname); + goto punt; + + err_map: + eid = NSAUERR3520; + rv = NSAERRCMAP; + nsadbCloseCertUsers(authdb, 0); + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adb->adb_dbname); + goto punt; +} + +NSAPI_PUBLIC int nsadbRemoveCert(NSErr_t * errp, void * authdb, + void * username, CertObj_t * coptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * keyptr = 0; /* pointer to cert record key */ + int keylen; /* length of cert record key */ + int rv; + int rv2; + + /* If a username is specified, require it to match */ + if (username && strcmp((char *)username, coptr->co_username)) { + return 0; + } + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&coptr->co_issuer, &coptr->co_subject, + &keylen, &keyptr); + + if (adb->adb_certdb == NULL) { + rv = nsadbOpenCerts(errp, authdb, ADBF_CWRITE); + if (rv < 0) goto punt; + } + + /* Remove the username-to-cert id entry from Certs.nm */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CWRITE); + if (rv < 0) goto punt; + rv = nsadbRemoveCertUser(errp, authdb, coptr->co_username); + nsadbCloseCertUsers(authdb, 0); + + /* Free the cert id value, if any */ + rv = 0; + if (coptr->co_certid != 0) { + rv = ndbFreeId(errp, adb->adb_certdb, + keylen, keyptr, coptr->co_certid); + } + + /* Delete the cert record */ + rv2 = ndbDeleteName(errp, adb->adb_certdb, 0, keylen, keyptr); + + punt: + if (keyptr) { + FREE(keyptr); + } + return (rv) ? rv : rv2; +} + +NSAPI_PUBLIC int nsadbRemoveUserCert(NSErr_t * errp, + void * authdb, char * username) +{ + CertObj_t * coptr = 0; + USI_t certid = 0; + int rv; + + /* + * Open for read access at first. We don't want to create the + * database if it's not already there. This will do nothing + * if the database is already open for write, since that implies + * read access as well. + */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + + /* Find a certificate mapping id for the given username */ + rv = nsadbFindCertUser(errp, authdb, username, &certid); + if (rv < 0) goto punt; + + /* Look up the mapping from the mapping id */ + rv = nsadbGetCertById(errp, authdb, certid, &coptr); + if (rv < 0) goto punt; + + /* It's there, so remove it. This will re-open for write if needed. */ + rv = nsadbRemoveCert(errp, authdb, (void *)username, coptr); + + punt: + + if (coptr != 0) { + nsadbFreeCertObj(coptr); + } + + return rv; +} + +#endif /* defined(CLIENT_AUTH) */ diff --git a/lib/libaccess/nsdb.cpp b/lib/libaccess/nsdb.cpp new file mode 100644 index 00000000..0479b8f3 --- /dev/null +++ b/lib/libaccess/nsdb.cpp @@ -0,0 +1,836 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsdb.c) + * + * This provides access to a Netscape server database. + * A server database is composed of two (libdbm) DB files. One + * of these (<dbname>.db) contains records indexed by a string + * key. These records contain the primary information in the + * database. A second DB file (<dbname>.id) is used to map an + * integer id value to a string key, which can then be used to + * locate a record in the first file. + * + * Normally the records in the primary DB file will contain the + * id values which are used to key the id-to-name DB. When this + * is the case, it is possible to construct the id-to-name DB from + * the primary DB file, and an interface is provided to facilitate + * this. + */ + +#include <stdio.h> +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#define __PRIVATE_NSDB +#include <libaccess/nsdb.h> + +#include <errno.h> + +#define NDBMODE 0644 /* mode for creating files */ + +char * NSDB_Program = "NSDB"; /* NSDB facility name */ + +NSPR_BEGIN_EXTERN_C + +/* + * Description (ndbClose) + * + * This function closes the specified database. This involves + * closing the primary and id-to-name DB files, and freeing the + * NSDB_t object. + * + * Arguments: + * + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * + */ + +void ndbClose(void * ndb, int flags) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + + if (ndbp->ndb_flags & (NDBF_WRNAME|NDBF_RDNAME)) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + } + + if (ndbp->ndb_flags & (NDBF_WRID|NDBF_RDID)) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + + if (ndbp->ndb_pname) { + FREE(ndbp->ndb_pname); + } + + if (ndbp->ndb_iname) { + FREE(ndbp->ndb_iname); + } + + FREE(ndbp); +} + +/* + * Description (ndbEnumerate) + * + * This function is called to enumerate the records of the primary + * DB file to a caller-specified function. The function specified + * by the caller is called with the name (key), length and address + * of each record in the primary DB file. The 'flags' argument can + * be used to select normal data records, metadata records, or both. + * If the 'flags' value is zero, only normal data records are + * enumerated. The function specified by the caller returns -1 to + * terminate the enumeration, 0 to continue it, or +1 to restart + * the enumeration from the beginning. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - bit flags: + * NDBF_ENUMNORM - normal data records + * NDBF_ENUMMETA - metadata records + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if an + * error frame list was provided by the caller. + */ + +int ndbEnumerate(NSErr_t * errp, void * ndb, int flags, void * argp, +#ifdef UnixWare + ArgFn_ndbEnum func) /* for ANSI C++ standard, see nsdb.h */ +#else + int (*func)(NSErr_t * ferrp, void * parg, + int namelen, char * name, + int reclen, char * recptr)) +#endif +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int rv; + int dbflag; + + /* Is the user DB open for reading names? */ + if (!(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDNAME); + if (rv) goto punt; + } + + if (flags == 0) flags = NDBF_ENUMNORM; + + for (dbflag = R_FIRST; ; dbflag = (rv > 0) ? R_FIRST : R_NEXT) { + + /* Retrieve the next (first) record from the primary DB */ + rv = (*ndbp->ndb_pdb->seq)(ndbp->ndb_pdb, &key, &rec, dbflag); + if (rv) break; + + /* Is this a metadata record? */ + if (*(char *)key.data == NDB_MDPREFIX) { + + /* Yes, skip it if metadata was not requested */ + if (!(flags & NDBF_ENUMMETA)) continue; + } + else { + /* Skip normal data if not requested */ + if (!(flags & NDBF_ENUMNORM)) continue; + } + + /* Pass this record to the caller's function */ + rv = (*func)(errp, argp, + key.size, (char *)key.data, rec.size, (char *)rec.data); + if (rv < 0) break; + } + + /* Indicate success */ + rv = 0; + + punt: + return rv; +} + +/* + * Description (ndbFindName) + * + * This function retrieves from the database a record with the + * specified key. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of the key, including null + * terminator if any + * name - pointer to the key of the desired record + * reclen - pointer to returned record length + * recptr - pointer to returned record pointer + * + * Returns: + * + * If successful, the return value is zero, and the length and + * address of the returned record are returned through reclen and + * recptr. Otherwise the return value is non-zero, and an error + * frame is generated if an error frame list was provided by the + * caller. + * + * Notes: + * + * The record buffer is dynamically allocated and is freed + * automatically when the database is closed. + */ + +int ndbFindName(NSErr_t * errp, void * ndb, int namelen, char * name, + int * reclen, char **recptr) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int eid; /* error id code */ + int rv; /* result value */ + + /* Is the user DB open for reading names? */ + if (!(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDNAME); + if (rv) goto punt; + } + + /* Set up record key. Include the terminating null byte. */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Initialize record buffer descriptor */ + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_pget; + + /* Return record length and address */ + if (reclen) *reclen = rec.size; + if (recptr) *recptr = (char *)rec.data; + + /* Indicate success */ + rv = 0; + + punt: + return rv; + + err_pget: + eid = NSDBERR1000; + rv = NDBERRGET; + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, name); + goto punt; +} + +/* + * Description (ndbIdToName) + * + * This function looks up a specified id in the id-to-name DB + * file, and returns the associated name string. This name + * can be used to retrieve a record using ndbFindName(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * id - id to look up + * plen - pointer to returned length of name + * (may be null, length includes null terminator + * in a string) + * pname - pointer to returned name string pointer + * + * Returns: + * + * The return value is zero if the operation is successful. An + * error is indicated by a negative return value (see nsdberr.h), + * and an error frame is generated if an error frame list was + * provided by the caller. + */ + +int ndbIdToName(NSErr_t * errp, + void * ndb, unsigned int id, int * plen, char **pname) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + char * name = 0; + int namelen = 0; + uint32 myid = id - 1; + int eid; /* error id code */ + int rv; /* result value */ + + /* Is the id-to-name DB open for reading ids? */ + if (!(ndbp->ndb_flags & NDBF_RDID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDID); + if (rv) goto punt; + } + + /* Set up record key */ +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(myid); +#endif + key.data = (void *)&myid; + key.size = sizeof(myid); + + /* Initialize record buffer descriptor */ + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_idb->get)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_iget; + + /* Get the name pointer (terminating null is part of the name) */ + namelen = rec.size; + name = (char *) rec.data; + + punt: + /* Return name length and size if requested */ + if (plen) *plen = namelen; + if (pname) *pname = name; + + return rv; + + err_iget: + eid = NSDBERR1100; + rv = NDBERRGET; + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_iname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbInitPrimary) + * + * This function creates and initializes the primary DB file. + * Initialization involves writing any required metadata records. + * Currently there is a ?dbtype record, which specifies the nsdb + * version number, and a database type and version number that + * were passed as arguments to ndbOpen(). There is also a + * ?idmap record, which contains an allocation bitmap for id values + * used as keys in the associated id-to-name DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if an + * error frame list was provided by the caller. + */ + +int ndbInitPrimary(NSErr_t * errp, void * ndb) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32 m; + int i; +#endif + int eid; /* error id code */ + int rv; /* result value */ + uint32 dbtype[4]; + + /* Error if the primary DB is marked as existing already */ + if (!(ndbp->ndb_flags & NDBF_NONAME)) goto err_exists; + + /* First create the primary DB file */ + ndbp->ndb_pdb = dbopen(ndbp->ndb_pname, O_RDWR | O_CREAT | O_TRUNC, + NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open; + + /* Generate data for the ?dbtype record */ + dbtype[0] = NDB_VERSION; + dbtype[1] = ndbp->ndb_dbtype; + dbtype[2] = ndbp->ndb_version; + dbtype[3] = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + for (i = 0; i < 4; ++i) { + m = dbtype[i]; + M_32_SWAP(m); + dbtype[i] = m; + } +#endif + + /* Set up descriptors for the ?dbtype record key and data */ + key.data = (void *)NDB_DBTYPE; + key.size = strlen(NDB_DBTYPE) + 1; + + rec.data = (void *)dbtype; + rec.size = sizeof(dbtype); + + /* Write the ?dbtype record out */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mput1; + + /* Write out an empty ?idmap record */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Write the ?idmap record */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mput2; + + /* Close the DB file */ + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + + /* Clear the flag that says the primary DB file does not exist */ + ndbp->ndb_flags &= ~(NDBF_NONAME|NDBF_RDNAME|NDBF_WRNAME); + + /* Indicate success */ + return 0; + + err_exists: + /* Primary database already exists */ + eid = NSDBERR1200; + rv = NDBERREXIST; + nserrGenerate(errp, rv, eid, NSDB_Program, 1, ndbp->ndb_pname); + return rv; + + err_open: + /* Error opening primary database for write */ + eid = NSDBERR1220; + rv = NDBERROPEN; + goto err_dbio; + + err_mput1: + /* Error writing "?dbtype" record */ + eid = NSDBERR1240; + rv = NDBERRMDPUT; + goto err_dbio; + + err_mput2: + /* Error writing "?idmap" record */ + eid = NSDBERR1260; + rv = NDBERRMDPUT; + goto err_dbio; + + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + + /* Close the primary DB file if it exists */ + if (ndbp->ndb_pdb) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~(NDBF_RDNAME|NDBF_WRNAME); + } + + /* Delete the file */ + system_unlink(ndbp->ndb_pname); + return rv; +} + +/* + * Description (ndbOpen) + * + * This function opens a server database by name. The specified + * name may be the name of the primary DB file, or the name + * without the ".db" suffix. This function will attempt to open + * both the primary and the id-to-name DB files for read access. + * If either of the DB files do not exist, they are not created + * here, but a handle for the database will still be returned. + * The DB files will be created when a subsequent access writes + * to the database. The caller also specifies an application + * database type, which is checked against a value stored in + * in the database metadata, if the primary DB file exists, or + * which is stored in the file metadata when the file is created. + * A type-specific version number is passed and returned. The + * value passed will be stored in the file metadata if it is + * subsequently created. If the file exists, the value in the + * file metadata is returned, and it is the caller's responsibility + * to interpret it. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * dbname - primary DB filename + * flags - (currently unused - should be zero) + * dbtype - application DB type (NDB_TYPE_xxxxx) + * version - (in/out) type-specific version number + * + * Returns: + * + * A handle that can be used for subsequent accesses to the database + * is returned, or 0, if an error occurs, and an error frame is + * generated if an error frame list was provided by the caller. + */ + +void * ndbOpen(NSErr_t * errp, + char * dbname, int flags, int dbtype, int * version) +{ + NSDB_t * ndbp = 0; /* database object pointer */ + char * pname = 0; /* primary DB file name */ + char * iname = 0; /* id-to-name DB file name */ + int namelen; + uint32 dbtrec[4]; + uint32 m; + DBT key; + DBT rec; + int eid; /* error id code */ + int rv; /* result value */ + + /* Get the database name */ + namelen = strlen(dbname); + if (!strcmp(&dbname[namelen-3], ".db")) { + namelen -= 3; + } + + /* Get the primary DB file name */ + pname = (char *)MALLOC(namelen + 4); + if (pname == 0) goto err_nomem1; + strncpy(pname, dbname, namelen); + strcpy(&pname[namelen], ".db"); + + /* Get the id-to-name DB file name */ + iname = (char *)MALLOC(namelen + 4); + if (iname == 0) goto err_nomem2; + strncpy(iname, dbname, namelen); + strcpy(&iname[namelen], ".id"); + + /* Allocate the database object */ + ndbp = (NSDB_t *)MALLOC(sizeof(NSDB_t)); + if (ndbp == 0) goto err_nomem3; + + /* Initialize the database object */ + ndbp->ndb_pname = pname; + ndbp->ndb_pdb = 0; + ndbp->ndb_iname = iname; + ndbp->ndb_idb = 0; + ndbp->ndb_flags = 0; + ndbp->ndb_dbtype = dbtype; + ndbp->ndb_version = (version) ? *version : 0; + + /* Open the primary DB file */ + ndbp->ndb_pdb = dbopen(pname, O_RDONLY, NDBMODE, DB_HASH, 0); + + /* Was it there? */ + if (ndbp->ndb_pdb) { + + /* Retrieve the ?dbtype record */ + key.data = (void *)NDB_DBTYPE; + key.size = strlen(NDB_DBTYPE) + 1; + + rec.data = 0; + rec.size = 0; + + /* Read the ?dbtype record */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Check it out */ + if (rec.size < 16) goto err_fmt; + + /* Copy data to an aligned area */ + memcpy((void *)dbtrec, rec.data, sizeof(dbtrec)); + + /* Get the NSDB version number */ + m = dbtrec[0]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* Assume forward compatibility with versions up to current + 0.5 */ + if (m > (NDB_VERSION + 5)) goto err_vers; + + /* XXX Assume infinite backward compatibility */ + + /* Get the application database type */ + m = dbtrec[1]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* It's got to match */ + if (m != dbtype) goto err_type; + + /* Get the type-specific version number */ + m = dbtrec[3]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* Don't check it. Just return it. */ + if (version) *version = m; + + /* The value in dbtrec[3] is currently ignored */ + + /* Mark the primary DB file open for read access */ + ndbp->ndb_flags |= NDBF_RDNAME; + } + else { + /* Indicate that the primary DB file does not exist */ + ndbp->ndb_flags |= NDBF_NONAME; + } + + return (void *)ndbp; + + err_nomem1: + eid = NSDBERR1400; + rv = NDBERRNOMEM; + goto err_nomem; + + err_nomem2: + eid = NSDBERR1420; + rv = NDBERRNOMEM; + goto err_nomem; + + err_nomem3: + eid = NSDBERR1440; + rv = NDBERRNOMEM; + err_nomem: + nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_mdget: + eid = NSDBERR1460; + rv = NDBERRMDGET; + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, + system_errmsg()); + goto err_ret; + + err_fmt: + eid = NSDBERR1480; + rv = NDBERRMDFMT; + goto err_ret; + + err_vers: + { + char vnbuf[16]; + + eid = NSDBERR1500; + rv = NDBERRVERS; + sprintf(vnbuf, "%d", (int)m); + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, vnbuf); + } + goto punt; + + err_type: + eid = NSDBERR1520; + rv = NDBERRDBTYPE; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSDB_Program, 1, ndbp->ndb_pname); + goto punt; + + punt: + /* Error clean-up */ + if (pname) FREE(pname); + if (iname) FREE(iname); + if (ndbp) { + /* Close the DB files if we got as far as opening them */ + if (ndbp->ndb_pdb) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + } + if (ndbp->ndb_idb) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + FREE(ndbp); + } + return 0; +} + +/* + * Description (ndbReOpen) + * + * This function is called to ensure that the primary DB file + * and/or the id-to-name DB file are open with specified access + * rights. For example, a file may be open for read, and it needs + * to be open for write. Both the primary and id-to-name DB files + * can be manipulated with a single call. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned (NDBERRxxxx - see nsdb.h). If an error + * list is provided, an error frame will be generated when the + * return value is non-zero. + */ + +int ndbReOpen(NSErr_t * errp, void * ndb, int flags) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + char * dbname; /* database name pointer */ + int eid; + int rv; + + /* Want to read or write the primary DB file? */ + if (flags & (NDBF_RDNAME|NDBF_WRNAME)) { + + /* Need to open for write? */ + if ((flags & NDBF_WRNAME) && !(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* If it's already open for read, close it first */ + if (ndbp->ndb_flags & NDBF_RDNAME) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~NDBF_RDNAME; + } + + /* Create it if it doesn't exist */ + if (ndbp->ndb_flags & NDBF_NONAME) { + rv = ndbInitPrimary(errp, ndb); + if (rv) goto err_init; + } + + /* Open primary DB file for write access */ + dbname = ndbp->ndb_pname; + ndbp->ndb_pdb = dbopen(dbname, O_RDWR, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open1; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= (NDBF_RDNAME|NDBF_WRNAME); + } + + /* Need to open for read? */ + if ((flags & NDBF_RDNAME) && !(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* If it's already open for write, close it first */ + if (ndbp->ndb_flags & NDBF_WRNAME) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~(NDBF_RDNAME|NDBF_WRNAME); + } + + /* Open primary DB file for read access */ + dbname = ndbp->ndb_pname; + ndbp->ndb_pdb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open2; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= NDBF_RDNAME; + } + } + + /* Want to read or write the id-to-name DB file? */ + if (flags & (NDBF_RDID|NDBF_WRID)) { + + /* Need to open for write? */ + if ((flags & NDBF_WRID) && !(ndbp->ndb_flags & NDBF_WRID)) { + + /* + * If it's not open for read yet, try to open it for read + * in order to find out if it exists. + */ + if (!(ndbp->ndb_flags & NDBF_RDID)) { + + /* Open id-to-name DB file for read access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH,0); + + /* Does it exist? */ + if (ndbp->ndb_idb == 0) { + + /* No, create it */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname,O_RDWR | O_CREAT | O_TRUNC, + NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open3; + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + else { + /* Mark it open for read */ + ndbp->ndb_flags |= NDBF_RDID; + } + } + + /* If it's already open for read, close it first */ + if (ndbp->ndb_flags & NDBF_RDID) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + ndbp->ndb_flags &= ~NDBF_RDID; + } + + /* Open id-to-name DB file for write access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDWR, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open4; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= (NDBF_RDID|NDBF_WRID); + } + + /* Need to open for read? */ + if ((flags & NDBF_RDID) && !(ndbp->ndb_flags & NDBF_RDID)) { + + /* If it's already open for write, close it first */ + if (ndbp->ndb_flags & NDBF_WRID) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + ndbp->ndb_flags &= ~(NDBF_RDID|NDBF_WRID); + } + + /* Open id-to-name DB file for read access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open5; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= NDBF_RDID; + } + } + + /* Successful completion */ + return 0; + + /* Begin error handlers */ + + err_init: /* failed to create primary DB file */ + (void)nserrGenerate(errp, NDBERRPINIT, NSDBERR1600, NSDB_Program, + 1, + ndbp->ndb_pname /* primary DB filename */ + ); + rv = NDBERRPINIT; + goto punt; + + err_open1: + eid = NSDBERR1620; + goto err_open; + + err_open2: + eid = NSDBERR1640; + goto err_open; + + err_open3: + eid = NSDBERR1660; + goto err_open; + + err_open4: + eid = NSDBERR1680; + goto err_open; + + err_open5: + eid = NSDBERR1700; + goto err_open; + + err_open: /* database open error */ + rv = NDBERROPEN; + (void)nserrGenerate(errp, NDBERROPEN, eid, NSDB_Program, + 2, dbname, system_errmsg()); + + punt: + return rv; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/nsdbmgmt.cpp b/lib/libaccess/nsdbmgmt.cpp new file mode 100644 index 00000000..c1151e08 --- /dev/null +++ b/lib/libaccess/nsdbmgmt.cpp @@ -0,0 +1,685 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsdbmgmt.h) + * + * The file describes the interface for managing information in + * a Netscape (server) database. A database is composed of + * two (libdbm) DB files. One of these (<dbname>.db) contains + * records indexed by a string key. These records contain the + * primary information in the database. A second DB file + * (<dbname>.id) is used to map an integer id value to a string + * key, which can then be used to locate a record in the first file. + * The interface for retrieving information from a database is + * described in nsdb.h. + */ + +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#define __PRIVATE_NSDB +#include <libaccess/nsdbmgmt.h> +#include <base/util.h> + +/* + * Description (ndbAllocId) + * + * This function allocates a unique id to be associated with a + * name in the primary DB file. An id bitmap is maintained in + * the primary DB file as a metadata record, and an entry is + * created in the id-to-name DB for the assigned id and the + * specified name. An allocated id value is always non-zero. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of key of the desired record, + * including null terminator if any + * name - pointer to the key of the desired record + * id - pointer to returned id value + * + * Returns: + * + * If successful, the return value is zero, and the allocated id + * is returned through 'id'. Otherwise a non-zero error code is + * returned (NDBERRxxxx - see nsdb.h). If an error list is + * provided, an error frame will be generated when the return + * value is non-zero. + */ + +int ndbAllocId(NSErr_t * errp, + void * ndb, int namelen, char * name, unsigned int * id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + unsigned char * idmap; + unsigned char * newmap = 0; + int m; + int mmsk; + uint32 idval; + int myid; + int i, n; + int rv; + long eid; + + /* + * Ensure that the name does not start with the metadata + * prefix character. + */ + if (!name || (name[0] == NDB_MDPREFIX)) goto err_name; + + /* + * Read the primary DB file metadata record containing the id + * allocation bitmap. + */ + + /* + * We need the primary and the id-to-name DB files open for write + * (and implicitly read) access. + */ + if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID)) + != (NDBF_WRNAME|NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID)); + if (rv < 0) goto punt; + } + + /* Set the key to the id allocation bitmap record name */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Search for an available id in the bitmap */ + n = rec.size; + idmap = (unsigned char *)rec.data; + + for (i = 0, m = 0; i < n; ++i) { + + m = idmap[i]; + if (m != 0) break; + } + + /* Did we find a byte with an available bit? */ + if (m == 0) { + + /* No, need to grow the bitmap */ + newmap = (unsigned char *)MALLOC(rec.size + 32); + if (newmap == 0) goto err_nomem1; + + /* Initialize free space at the beginning of the new map */ + for (i = 0; i < 32; ++i) { + newmap[i] = 0xff; + } + + /* Copy the old map after it */ + n += 32; + for ( ; i < n; ++i) { + newmap[i] = idmap[i-32]; + } + + /* Set i and m to allocate the new highest id value */ + i = 0; + m = 0xff; + } + else { + + /* + * It's unfortunate, but it appears to be necessary to copy the + * the ?idmap record into a new buffer before updating it, rather + * than simply updating it in place. The problem is that the + * libdbm put routine deletes the old record and then re-inserts + * it. But once it has deleted the old record, it may take the + * opportunity to move another record into the space that the + * old record occupied, which is the same space that the new + * record occupies. So the new record data is overwritten before + * new record is inserted. :-( + */ + + newmap = (unsigned char *)MALLOC(rec.size); + if (newmap == 0) goto err_nomem2; + + memcpy((void *)newmap, (void *)idmap, rec.size); + } + + /* Calculate the id associated with the low-order bit of byte i */ + myid = (n - i - 1) << 3; + + /* Find the first free (set) bit in that word */ + for (mmsk = 1; !(m & mmsk); mmsk <<= 1, myid += 1) ; + + /* Clear the bit */ + m &= ~mmsk; + newmap[i] = m; + + /* Write the bitmap back out */ + + rec.data = (void *)newmap; + rec.size = n; + + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + + /* Check for error on preceding put operation */ + if (rv) goto err_putpdb; + + /* Create the key for the id-to-name record */ + idval = myid; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + + key.data = (void *)&idval; + key.size = sizeof(uint32); + + rec.data = (void *)name; + rec.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Write the id-to-name record */ + rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_putidb; + + /* Return the id value + 1, to avoid returning a zero id */ + if (id) *id = myid + 1; + + punt: + + /* Free the new map space if any */ + if (newmap) { + FREE(newmap); + } + + return rv; + + err_name: /* invalid name parameter */ + eid = NSDBERR2000; + rv = NDBERRNAME; + if (name == 0) { + name = "(null)"; + } + else if ((namelen > 0) && (namelen != strlen(name) + 1)) { + name = "(unprintable)"; + } + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + name /* name string */ + ); + goto punt; + + err_mdget: /* error on get from primary DB file */ + eid = NSDBERR2020; + rv = NDBERRMDGET; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + (char *)key.data /* key name string */ + ); + goto punt; + + err_nomem1: + eid = NSDBERR2040; + goto err_nomem; + + err_nomem2: + eid = NSDBERR2060; + err_nomem: /* insufficient memory */ + rv = NDBERRNOMEM; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_putpdb: /* error on put to primary DB file */ + eid = NSDBERR2080; + rv = NDBERRMDPUT; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + (char *)key.data /* key name string */ + ); + goto punt; + + err_putidb: /* error on put to id-to-name DB */ + { + char idstring[16]; + + eid = NSDBERR2100; + rv = NDBERRIDPUT; + + util_sprintf(idstring, "%d", myid); + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_iname, /* id-to-name DB file */ + idstring /* id value for key */ + ); + } + goto punt; +} + +/* + * Description (ndbDeleteName) + * + * This function deletes a named record from the primary DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * namelen - length of name key, including null + * terminator if any + * name - pointer to name key + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned (NDBERRxxxx - see nsdberr.h). If an error + * list is provided, an error frame will be generated when the + * return value is non-zero. + */ + +int ndbDeleteName(NSErr_t * errp, + void * ndb, int flags, int namelen, char * name) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + int eid; + int rv; + + /* Is the primary DB open for write access? */ + if (!(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRNAME); + if (rv) goto punt; + } + + /* Set up the key descriptor */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Delete the record from the primary DB file */ + rv = (*ndbp->ndb_pdb->del)(ndbp->ndb_pdb, &key, 0); + if (rv) goto err_delpdb; + + /* Successful completion */ + return 0; + + /* Begin error handlers */ + + err_delpdb: /* error deleting record from primary DB */ + eid = NSDBERR2200; + rv = NDBERRNMDEL; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB name */ + (char *)key.data /* primary key */ + ); + punt: + return rv; +} + +/* + * Description (ndbFreeId) + * + * This function frees an id value associated with a name in the + * primary DB file. It is normally called when the named record + * is being deleted from the primary DB file. It deletes the + * record in the id-to-name DB file that is keyed by the id value, + * and updates the id allocation bitmap in the primary DB file. + * The caller may specify the name that is associated with the id + * value, in which case the id-to-name record will be fetched, + * and the name matched, before the record is deleted. Alternatively + * the name parameter can be specified as zero, and id-to-name + * record will be deleted without a check. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of name (including null terminator) + * name - name associated with the id value (optional) + * id - id value to be freed + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if the + * caller provided an error frame list. + */ + +int ndbFreeId(NSErr_t * errp, + void * ndb, int namelen, char * name, unsigned int id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + char * recname; + DBT key; + DBT rec; + uint32 idval; + int reclen; + int mmsk; + unsigned char * idmap = 0; + int i; + int eid; + int rv; + + /* + * We need the primary and the id-to-name DB files open for write + * (and implicitly read) access. + */ + if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID)) + != (NDBF_WRNAME|NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID)); + if (rv) goto punt; + } + + /* Was the name for this id value provided by the caller? */ + if (name) { + + /* Get length of name if not provided */ + if (namelen <= 0) namelen = strlen(name) + 1; + + /* Yes, look up the id and check for a match */ + rv = ndbIdToName(errp, ndb, id, &reclen, &recname); + if (rv < 0) goto punt; + + /* Fail if the supplied name doesn't match */ + if ((namelen != reclen) || + strncmp(recname, name, reclen)) goto err_badid1; + } + + /* Caller views the id space as starting at 1, but we start at 0 */ + id -= 1; + + /* Create the key for the id-to-name record */ + idval = id; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + + key.data = (void *)&idval; + key.size = sizeof(uint32); + + /* Delete the id-to-name record */ + rv = (*ndbp->ndb_idb->del)(ndbp->ndb_idb, &key, 0); + if (rv) goto err_del; + + /* Set the key to the id allocation bitmap record name */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Make sure the id is in the range of the bitmap */ + i = (rec.size << 3) - id - 1; + if (i < 0) goto err_badid2; + + /* + * See comment in ndbAllocId() about updating ?idmap. Bottom line + * is: we have to copy the record before updating it. + */ + + idmap = (unsigned char *)MALLOC(rec.size); + if (idmap == 0) goto err_nomem; + + memcpy((void *)idmap, rec.data, rec.size); + + /* Calculate the index of the byte with this id's bit */ + i >>= 3; + + /* Calculate the bitmask for the bitmap byte */ + mmsk = 1 << (id & 7); + + /* Set the bit in the bitmap */ + idmap[i] |= mmsk; + + /* Write the bitmap back out */ + + rec.data = (void *)idmap; + + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdput; + + punt: + + if (idmap) { + FREE(idmap); + } + + return rv; + + err_badid1: + /* Name associated with id doesn't match supplied name */ + eid = NSDBERR2300; + rv = NDBERRBADID; + goto err_id; + + err_del: + /* Error deleting id-to-name record */ + eid = NSDBERR2320; + rv = NDBERRIDDEL; + goto err_dbio; + + err_mdget: + /* Error reading id bitmap from primary DB file */ + eid = NSDBERR2340; + rv = NDBERRMDGET; + goto err_dbio; + + err_badid2: + eid = NSDBERR2360; + rv = NDBERRBADID; + err_id: + { + char idbuf[16]; + + util_sprintf(idbuf, "%d", id); + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, idbuf); + } + goto punt; + + err_nomem: + eid = NSDBERR2380; + rv = NDBERRNOMEM; + nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_mdput: + eid = NSDBERR2400; + rv = NDBERRMDPUT; + goto err_dbio; + + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbRenameId) + * + * This function changes the name associated with a specified id + * int the id-to-name DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of new name string, including + * null terminator if any + * newname - pointer to the new name string + * id - id value to be renamed + * + * Returns: + * + * The return value is zero if the operation is successful. An + * error is indicated by a non-zero return value, and an error + * frame is generated if the caller provided an error frame list. + */ + +int ndbRenameId(NSErr_t * errp, + void * ndb, int namelen, char * newname, unsigned int id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + uint32 idval = id - 1; + int eid; + int rv; + + /* + * Ensure that the name does not start with the metadata + * prefix character. + */ + if (!newname || (newname[0] == NDB_MDPREFIX)) goto err_name; + + /* + * We need the id-to-name DB file open for write + * (and implicitly read) access. + */ + if (!(ndbp->ndb_flags & NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRID); + if (rv) goto punt; + } + + /* Set up record key */ +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + key.data = (void *)&idval; + key.size = sizeof(uint32); + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_idb->get)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_idget; + + /* Set up to write the new name */ + rec.data = (void *)newname; + rec.size = (namelen > 0) ? namelen : (strlen(newname) + 1); + + /* Write the id-to-name record */ + rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_idput; + + punt: + return rv; + + err_name: + eid = NSDBERR2500; + rv = NDBERRNAME; + if (newname == 0) newname = "(null)"; + else if ((namelen > 0) && (namelen != (strlen(newname) + 1))) { + newname = "(unprintable)"; + } + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + newname /* name string */ + ); + goto punt; + + err_idget: + /* Error getting id record from id-to-name database */ + eid = NSDBERR2520; + rv = NDBERRGET; + goto err_dbio; + + err_idput: + /* Error putting id record back to id-to-name database */ + eid = NSDBERR2540; + rv = NDBERRIDPUT; + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbStoreName) + * + * This function stores a record, keyed by a specified name, in the + * primary DB file. The record will overwrite any existing record + * with the same key, unless NDBF_NEWNAME, is included in the 'flags' + * argument. If NDBF_NEWNAME is set, and the record already exists, + * it is not overwritten, and an error is returned. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - bit flags: + * NDBF_NEWNAME - name is new + * namelen - length of name key, including null + * terminator if any + * name - pointer to name key + * reclen - length of the record data + * recptr - pointer to the record data + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if the + * caller provided an error frame list. + */ + +int ndbStoreName(NSErr_t * errp, void * ndb, int flags, + int namelen, char * name, int reclen, char * recptr) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int eid; + int rv; + + /* Is the primary DB open for write access? */ + if (!(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRNAME); + if (rv) goto punt; + } + + /* Set up the key and record descriptors */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + rec.data = (void *)recptr; + rec.size = reclen; + + /* Write the record to the primary DB file */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, + (flags & NDBF_NEWNAME) ? R_NOOVERWRITE : 0); + if (rv) goto err_put; + + punt: + return rv; + + err_put: + eid = NSDBERR2700; + rv = NDBERRPUT; + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} diff --git a/lib/libaccess/nseframe.cpp b/lib/libaccess/nseframe.cpp new file mode 100644 index 00000000..c878940b --- /dev/null +++ b/lib/libaccess/nseframe.cpp @@ -0,0 +1,207 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nseframe.c) + * + * This module is part of the NSACL_RES_ERROR facility. It contains functions + * for allocating, freeing, and managing error frame structures. It + * does not contain routines for generating error messages through + * the use of a message file. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/nserror.h" + +/* + * Description (nserrDispose) + * + * This function is used to dispose of an entire list of error + * frames when the error information is no longer needed. It + * does not free the list head, since it is usually not dynamically + * allocated. + * + * Arguments: + * + * errp - error frame list head pointer + */ + +void nserrDispose(NSErr_t * errp) +{ + /* Ignore null list head */ + if (errp == 0) return; + + while (errp->err_first) { + + nserrFFree(errp, errp->err_first); + } +} + +/* + * Description (nserrFAlloc) + * + * This is the default allocator for error frame structures. It + * calls an allocator function indicated by err_falloc, if any, + * or else uses MALLOC(). + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * + * Returns: + * + * The return value is a pointer to a cleared error frame. The + * frame will not have been added to the list referenced by errp. + * An allocation error is indicated by a null return value. + */ + +NSEFrame_t * nserrFAlloc(NSErr_t * errp) +{ + NSEFrame_t * efp; /* return error frame pointer */ + + /* Allocate the error frame */ + efp = (errp && errp->err_falloc) + ? (*errp->err_falloc)(errp) + : (NSEFrame_t *)MALLOC(sizeof(NSEFrame_t)); + + if (efp) { + /* Clear the error frame */ + memset((void *)efp, 0, sizeof(NSEFrame_t)); + } + + return efp; +} + +/* + * Description (nserrFFree) + * + * This function frees an error frame structure. If an error list + * head is specified, it first checks whether the indicated error + * frame is on the list, and removes it if so. If the ef_dispose + * field is non-null, the indicated function is called. The error + * frame is deallocated using either a function indicated by + * err_free in the list head, or FREE() otherwise. + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * efp - error frame pointer + */ + +void nserrFFree(NSErr_t * errp, NSEFrame_t * efp) +{ + NSEFrame_t **lefp; /* pointer to error frame pointer */ + NSEFrame_t * pefp; /* previous error frame on list */ + int i; + + /* Ignore null error frame pointer */ + if (efp == 0) return; + + /* Got a list head? */ + if (errp) { + + /* Yes, see if this frame is on the list */ + pefp = 0; + for (lefp = &errp->err_first; *lefp != 0; lefp = &pefp->ef_next) { + if (*lefp == efp) { + + /* Yes, remove it */ + *lefp = efp->ef_next; + if (errp->err_last == efp) errp->err_last = pefp; + break; + } + pefp = *lefp; + } + } + + /* Free strings referenced by the frame */ + for (i = 0; i < efp->ef_errc; ++i) { + if (efp->ef_errv[i]) { + FREE(efp->ef_errv[i]); + } + } + + /* Free the frame */ + if (errp && errp->err_ffree) { + (*errp->err_ffree)(errp, efp); + } + else { + FREE(efp); + } +} + +/* + * Description (nserrGenerate) + * + * This function is called to generate an error frame and add it + * to a specified error list. + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * retcode - return code (ef_retcode) + * errorid - error id (ef_errorid) + * program - program string pointer (ef_program) + * errc - count of error arguments (ef_errc) + * ... - values for ef_errv[] + * + * Returns: + * + * The return value is a pointer to the generated error frame, filled + * in with the provided information. An allocation error is indicated + * by a null return value. + */ + +NSEFrame_t * nserrGenerate(NSErr_t * errp, long retcode, long errorid, + char * program, int errc, ...) +{ + NSEFrame_t * efp; /* error frame pointer */ + char * esp; /* error string pointer */ + int i; + va_list ap; + + /* Null frame list head pointer means do nothing */ + if (errp == 0) { + return 0; + } + + /* Limit the number of values in ef_errv[] */ + if (errc > NSERRMAXARG) errc = NSERRMAXARG; + + /* Allocate the error frame */ + efp = nserrFAlloc(errp); + + /* Did we get it? */ + if (efp) { + + /* Yes, copy information into it */ + efp->ef_retcode = retcode; + efp->ef_errorid = errorid; + efp->ef_program = program; + efp->ef_errc = errc; + + /* Get the string arguments and copy them */ + va_start(ap, errc); + for (i = 0; i < errc; ++i) { + esp = va_arg(ap, char *); + efp->ef_errv[i] = STRDUP(esp); + } + + /* Add the frame to the list (if any) */ + if (errp) { + efp->ef_next = errp->err_first; + errp->err_first = efp; + if (efp->ef_next == 0) errp->err_last = efp; + } + } + + /* Return the error frame pointer */ + return efp; +} diff --git a/lib/libaccess/nsgmgmt.cpp b/lib/libaccess/nsgmgmt.cpp new file mode 100644 index 00000000..922b21bc --- /dev/null +++ b/lib/libaccess/nsgmgmt.cpp @@ -0,0 +1,434 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsgmgmt.c) + * + * This module contains routines for managing information in a + * Netscape group database. Information for a particular group + * is modified by retrieving the current information in the form + * of a group object (GroupObj_t), calling functions in this module, + * to modify the group object, and then calling groupStore() to + * write the information in the group object back to the database. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/nsdbmgmt.h" +#define __PRIVATE_NSGROUP +#include "libaccess/nsgmgmt.h" + +/* + * Description (groupAddMember) + * + * This function adds a member to a group object. The member may + * be another group or a user, expressed as a group id or user id, + * respectively. The 'isgid' argument is non-zero if the new + * member is a group, or zero if it is a user. + * + * Arguments: + * + * goptr - group object pointer + * isgid - non-zero if 'id' is a group id + * zero if 'id' is a user id + * id - group or user id to be added + * + * Returns: + * + * Returns zero if the specified member is already a direct member + * of the group. Returns one if the member was added successfully. + */ + +NSAPI_PUBLIC int groupAddMember(GroupObj_t * goptr, int isgid, USI_t id) +{ + USIList_t * uilptr; + int rv = 0; + + /* Point to the relevant uid or gid list */ + uilptr = (isgid) ? &goptr->go_groups : &goptr->go_users; + + /* Add the id to the selected list */ + rv = usiInsert(uilptr, id); + if (rv > 0) { + goptr->go_flags |= GOF_MODIFIED; + } + + return rv; +} + +/* + * Description (groupCreate) + * + * This function creates a group object, using information about + * the group provided by the caller. The strings passed for the + * group name and description may be on the stack. The group id + * is set to zero, but the group object is marked as being new. + * A group id will be assigned when groupStore() is called to add + * the group to a group database. + * + * Arguments: + * + * name - pointer to group name string + * desc - pointer to group description string + * + * Returns: + * + * A pointer to a dynamically allocated GroupObj_t structure is + * returned. + */ + +NSAPI_PUBLIC GroupObj_t * groupCreate(NTS_t name, NTS_t desc) +{ + GroupObj_t * goptr; /* group object pointer */ + + goptr = (GroupObj_t *)MALLOC(sizeof(GroupObj_t)); + if (goptr) { + goptr->go_name = (NTS_t)STRDUP((char *)name); + goptr->go_gid = 0; + goptr->go_flags = (GOF_MODIFIED | GOF_NEW); + if (desc) { + goptr->go_desc = (desc) ? (NTS_t)STRDUP((char *)desc) : 0; + } + UILINIT(&goptr->go_users); + UILINIT(&goptr->go_groups); + UILINIT(&goptr->go_pgroups); + } + + return goptr; +} + +/* + * Description (groupDeleteMember) + * + * This function removes a specified member from a group object's + * list of members. The member to be remove may be a group or a + * user, expressed as a group id or user id, respectively. The + * 'isgid' argument is non-zero if the member being removed is a + * group, or zero if it is a user. + * + * Arguments: + * + * goptr - pointer to group object + * isgid - non-zero if 'id' is a group id + * zero if 'id' is a user id + * id - group or user id to be removed + * + * Returns: + * + * The return value is zero if the specified member was not present + * in the group object, or one if the member was successfully removed. + */ + +NSAPI_PUBLIC int groupDeleteMember(GroupObj_t * goptr, int isgid, USI_t id) +{ + USIList_t * uilptr; /* pointer to list of member users or groups */ + int rv; /* return value */ + + /* Get pointer to appropriate list of ids */ + uilptr = (isgid) ? &goptr->go_groups : &goptr->go_users; + + /* Remove the specified id */ + rv = usiRemove(uilptr, id); + if (rv > 0) { + goptr->go_flags |= GOF_MODIFIED; + } + + return rv; +} + +/* + * Description (groupEncode) + * + * This function encodes a group object into a group DB record. + * + * Arguments: + * + * goptr - pointer to group object + * greclen - pointer to returned record length + * grecptr - pointer to returned record pointer + * + * Returns: + * + * The function return value is zero if successful. The length + * and location of the created attribute record are returned + * through 'greclen' and 'grecptr'. A non-zero function value + * is returned if there's an error. + */ + +NSAPI_PUBLIC int groupEncode(GroupObj_t * goptr, int * greclen, ATR_t * grecptr) +{ + int reclen; /* length of DB record */ + ATR_t rptr; /* DB record pointer */ + ATR_t rstart = 0; /* pointer to beginning of DB record */ + ATR_t glptr; /* saved pointer to UAT_GROUPS length */ + ATR_t gptr; /* saved pointer to after length at glptr */ + int gidlen; /* gid encoding length */ + int fllen; /* flags encoding length */ + USI_t dsclen; /* group description encoding length */ + USI_t nulen; /* member user count encoding length */ + USI_t nglen; /* member group count encoding length */ + int idcnt; /* count of user or group ids */ + USI_t * ids; /* pointer to array of user or group ids */ + int i; /* id index */ + int rv = -1; + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* GAT_GID attribute: tag, length, USI */ + gidlen = USILENGTH(goptr->go_gid); + reclen = (1 + 1 + gidlen); + + /* GAT_FLAGS attribute: tag, length, USI */ + fllen = USILENGTH(goptr->go_flags & GOF_DBFLAGS); + reclen += (1 + 1 + fllen); + + /* GAT_DESCRIPT attribute: tag, length, NTS */ + dsclen = NTSLENGTH(goptr->go_desc); + reclen += (1 + USILENGTH(dsclen) + dsclen); + + /* GAT_USERS attribute: tag, length, USI(count), USI(uid)... */ + idcnt = UILCOUNT(&goptr->go_users); + nulen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nulen + (5 * idcnt)); + + /* GAT_GROUPS attribute: tag, length, USI(count), USI(gid)... */ + idcnt = UILCOUNT(&goptr->go_groups); + nglen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nglen + (5 * idcnt)); + + /* GAT_PGROUPS attribute: tag, length, USI(count), USI(gid)... */ + idcnt = UILCOUNT(&goptr->go_pgroups); + nglen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nglen + (5 * idcnt)); + + /* Allocate the attribute record buffer */ + rptr = (ATR_t)MALLOC(reclen); + if (rptr) { + + /* Save pointer to start of record */ + rstart = rptr; + + /* Encode GAT_GID attribute */ + *rptr++ = GAT_GID; + *rptr++ = gidlen; + rptr = USIENCODE(rptr, goptr->go_gid); + + /* Encode GAT_FLAGS attribute */ + *rptr++ = GAT_FLAGS; + *rptr++ = fllen; + rptr = USIENCODE(rptr, (goptr->go_flags & GOF_DBFLAGS)); + + /* Encode GAT_DESCRIPT attribute */ + *rptr++ = GAT_DESCRIPT; + rptr = USIENCODE(rptr, dsclen); + rptr = NTSENCODE(rptr, goptr->go_desc); + + /* Encode GAT_USERS attribute */ + *rptr++ = GAT_USERS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of user members */ + idcnt = UILCOUNT(&goptr->go_users); + rptr = USIENCODE(rptr, idcnt); + + /* Generate user ids encodings */ + ids = UILLIST(&goptr->go_users); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_USERS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Encode GAT_GROUPS attribute */ + *rptr++ = GAT_GROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + idcnt = UILCOUNT(&goptr->go_groups); + rptr = USIENCODE(rptr, idcnt); + + /* Generate group ids encodings */ + ids = UILLIST(&goptr->go_groups); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_GROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Encode GAT_PGROUPS attribute */ + *rptr++ = GAT_PGROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + idcnt = UILCOUNT(&goptr->go_pgroups); + rptr = USIENCODE(rptr, idcnt); + + /* Generate group ids encodings */ + ids = UILLIST(&goptr->go_pgroups); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_PGROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Return record length and location if requested */ + if (greclen) *greclen = rptr - rstart; + if (grecptr) *grecptr = rstart; + + /* Indicate success */ + rv = 0; + } + + return rv; +} + +/* + * Description (groupRemove) + * + * This function is called to remove a group from a specified group + * database. Both the primary DB file and the id-to-name DB file + * are updated. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - (unused - must be zero) + * name - pointer to group name + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int groupRemove(NSErr_t * errp, void * groupdb, int flags, NTS_t name) +{ + GroupObj_t * goptr; /* group object pointer */ + int rv; + int rv2; + + /* First retrieve the group record */ + goptr = groupFindByName(errp, groupdb, name); + if (!goptr) { + /* Error - specified group not found */ + return NSAERRNAME; + } + + /* Free the group id value, if any */ + rv = 0; + if (goptr->go_gid != 0) { + rv = ndbFreeId(errp, groupdb, 0, (char *)name, goptr->go_gid); + } + + rv2 = ndbDeleteName(errp, groupdb, 0, 0, (char *)name); + + return (rv) ? rv : rv2; +} + +/* + * Description (groupStore) + * + * This function is called to store a group object in the database. + * If the object was created by groupCreate(), it is assumed to be + * a new group, the group account name must not match any existing + * group account names in the database, and a gid is assigned before + * adding the group to the database. If the object was created by + * groupFindByName(), the information in the group object will + * replace the existing database entry for the indicated group + * name. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - (unused - must be zero) + * goptr - group object pointer + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The group object remains intact in either + * case. + */ + +NSAPI_PUBLIC int groupStore(NSErr_t * errp, void * groupdb, int flags, GroupObj_t * goptr) +{ + ATR_t recptr = 0; + USI_t gid; + int reclen = 0; + int stflags = 0; + int eid; + int rv; + + /* If this is a new group, allocate a uid value */ + if (goptr->go_flags & GOF_NEW) { + + rv = ndbAllocId(errp, groupdb, 0, (char *)goptr->go_name, &gid); + if (rv) goto punt; + + goptr->go_gid = gid; + + /* Let the database manager know that this is a new entry */ + stflags = NDBF_NEWNAME; + } + + /* Convert the information in the group object to a DB record */ + rv = groupEncode(goptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* + * Store the record in the database under the group name. + * If this is a new entry, a group id to group name mapping + * also will be added to the id-to-name DB file. + */ + rv = ndbStoreName(errp, groupdb, stflags, + 0, (char *)goptr->go_name, reclen, (char *)recptr); + + FREE(recptr); + + if (rv == 0) { + goptr->go_flags &= ~(GOF_NEW | GOF_MODIFIED); + } + + punt: + return rv; + + err_nomem: + eid = NSAUERR2000; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; +} diff --git a/lib/libaccess/nsgroup.cpp b/lib/libaccess/nsgroup.cpp new file mode 100644 index 00000000..c67c85ba --- /dev/null +++ b/lib/libaccess/nsgroup.cpp @@ -0,0 +1,336 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsgroup.c) + * + * This module contains routines for accessing information in a + * Netscape group database. Group information is returned in the + * form of a group object (GroupObj_t), defined in nsauth.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_NSGROUP +#include "libaccess/nsgroup.h" + +/* + * Description (groupDecode) + * + * This function decodes an external group DB record into a + * dynamically allocated GroupObj_t structure. The DB record is + * encoded as an attribute record as defined in attrec.h. + * + * Arguments: + * + * name - pointer to group name string + * greclen - length of the group DB record, in octets + * grecptr - pointer to group DB record + * + * Returns: + * + * A pointer to the allocated GroupObj_t structure is returned. + */ + +NSAPI_PUBLIC GroupObj_t * groupDecode(NTS_t name, int greclen, ATR_t grecptr) +{ + ATR_t cp = grecptr; /* current pointer into DB record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + int i; /* group id index */ + int idcnt; /* count of user or group ids */ + USI_t * ids; /* pointer to array of ids */ + GroupObj_t * goptr; /* group object pointer */ + + /* Allocate a group object structure */ + goptr = (GroupObj_t *)MALLOC(sizeof(GroupObj_t)); + if (goptr) { + + goptr->go_name = (unsigned char *) STRDUP((char *)name); + goptr->go_gid = 0; + goptr->go_flags = GOF_MODIFIED; + goptr->go_desc = 0; + UILINIT(&goptr->go_users); + UILINIT(&goptr->go_groups); + UILINIT(&goptr->go_pgroups); + + /* Parse group DB record */ + while ((cp - grecptr) < greclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case GAT_GID: /* group id */ + cp = USIDECODE(cp, &goptr->go_gid); + break; + + case GAT_FLAGS: /* flags */ + cp = USIDECODE(cp, &goptr->go_flags); + break; + + case GAT_DESCRIPT: /* group description */ + cp = NTSDECODE(cp, &goptr->go_desc); + break; + + case GAT_USERS: /* member users of this group */ + + /* First get the number of user ids following */ + cp = USIDECODE(cp, (unsigned *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for user ids */ + ids = usiAlloc(&goptr->go_users, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + case GAT_GROUPS: /* member groups of this group */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, (unsigned *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for group ids */ + ids = usiAlloc(&goptr->go_groups, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + case GAT_PGROUPS: /* parent groups of this group */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, (USI_t *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for group ids */ + ids = usiAlloc(&goptr->go_pgroups, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + } + + return goptr; +} + +/* + * Description (groupEnumHelp) + * + * This is a local function that is called by NSDB during group + * database enumeration. It decodes group records into group + * objects, and presents them to the caller of groupEnumerate(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to GroupEnumArgs_t structure + * namelen - length of group record key, including null + * terminator + * name - group record key (group account name) + * reclen - length of group record + * recptr - pointer to group record contents + * + * Returns: + * + * Returns whatever value is returned from the upcall to the caller + * of groupEnumerate(). + */ + +static int groupEnumHelp(NSErr_t * errp, void * parg, + int namelen, char * name, int reclen, char * recptr) +{ + GroupEnumArgs_t * ge = (GroupEnumArgs_t *)parg; + GroupObj_t * goptr; /* group object pointer */ + int rv; + + goptr = groupDecode((NTS_t)name, reclen, (ATR_t)recptr); + + rv = (*ge->func)(errp, ge->user, goptr); + + if (!(ge->flags & GOF_ENUMKEEP)) { + FREE(goptr); + } + + return rv; +} + +/* + * Description (groupEnumerate) + * + * This function enumerates all of the groups in a specified group + * database, calling a caller-specified function with a group object + * for each group in the database. A 'flags' value of GOF_ENUMKEEP + * can be specified to keep the group objects around (not free them) + * after the caller's function returns. Otherwise, each group + * object is freed after being presented to the caller's function. + * The 'argp' argument is an opaque pointer, which is passed to + * the caller's function as 'parg' on each call, along with a + * group object pointer. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - bit flags: + * GOF_ENUMKEEP - keep group objects + * argp - passed to 'func' as 'parg' + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int groupEnumerate(NSErr_t * errp, void * groupdb, int flags, void * argp, + int (*func)(NSErr_t * ferrp, + void * parg, GroupObj_t * goptr)) +{ + int rv; + GroupEnumArgs_t args; + + args.groupdb = groupdb; + args.flags = flags; + args.func = func; + args.user = argp; + + rv = ndbEnumerate(errp, + groupdb, NDBF_ENUMNORM, (void *)&args, groupEnumHelp); + + return rv; +} + +/* + * Description (groupFindByName) + * + * This function looks up a group record for a specified group name, + * converts the group record to the internal group object form, and + * returns a pointer to the group object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * name - group name to find + * + * Returns: + * + * If successful, the return value is a pointer to a group object + * for the specified group. Otherwise it is 0. + */ + +NSAPI_PUBLIC GroupObj_t * groupFindByName(NSErr_t * errp, void * groupdb, NTS_t name) +{ + GroupObj_t * goptr = 0; + ATR_t grecptr; + int greclen; + int rv; + + /* Look up the group name in the database */ + rv = ndbFindName(errp, groupdb, 0, (char *)name, &greclen, (char **)&grecptr); + if (rv == 0) { + + /* Got the group record. Decode into a group object. */ + goptr = groupDecode(name, greclen, grecptr); + } + + return goptr; +} + +/* + * Description (groupFindByGid) + * + * This function looks up a group record for a specified group id, + * converts the group record to the internal group object form, and + * returns a pointer to the group object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * gid - group id to find + * + * Returns: + * + * If successful, the return value is a pointer to a group object + * for the specified group. Otherwise it is 0. + */ + +NSAPI_PUBLIC GroupObj_t * groupFindByGid(NSErr_t * errp, void * groupdb, USI_t gid) +{ + GroupObj_t * goptr = 0; + NTS_t name; + ATR_t grecptr; + int greclen; + int rv; + + /* Get the group account name corresponding to the gid */ + rv = ndbIdToName(errp, groupdb, gid, 0, (char **)&name); + if (rv == 0) { + + rv = ndbFindName(errp, groupdb, 0, (char *)name, &greclen, (char **)&grecptr); + if (rv == 0) { + + /* Got the group record. Decode into a group object. */ + goptr = groupDecode(name, greclen, grecptr); + } + } + + return goptr; +} + +/* + * Description (groupFree) + * + * This function is called to free a group object. Group objects + * are not automatically freed when a group database is closed. + * + * Arguments: + * + * goptr - group object pointer + * + */ + +NSAPI_PUBLIC void groupFree(GroupObj_t * goptr) +{ + if (goptr) { + + if (goptr->go_name) FREE(goptr->go_name); + if (goptr->go_desc) FREE(goptr->go_desc); + UILFREE(&goptr->go_users); + UILFREE(&goptr->go_groups); + UILFREE(&goptr->go_pgroups); + FREE(goptr); + } +} diff --git a/lib/libaccess/nslock.cpp b/lib/libaccess/nslock.cpp new file mode 100644 index 00000000..2cf0b00b --- /dev/null +++ b/lib/libaccess/nslock.cpp @@ -0,0 +1,268 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nslock.c) + * + * This modules provides an interprocess locking mechanism, based + * on a named lock. + */ + +#include "netsite.h" +#include "base/file.h" +#define __PRIVATE_NSLOCK +#include "nslock.h" +#include <assert.h> + +char * NSLock_Program = "NSLOCK"; + +#ifdef FILE_UNIX +/* + * The process-wide list of locks, NSLock_List, is protected by the + * critical section, NSLock_Crit. + */ +CRITICAL NSLock_Crit = 0; +NSLock_t * NSLock_List = 0; +#endif /* FILE_UNIX */ + +/* + * Description (nsLockOpen) + * + * This function is used to initialize a handle for a lock. The + * caller specifies a unique name for the lock, and a handle is + * returned. The returned handle should be used by only one + * thread at a time, i.e. if multiple threads in a process are + * using the same lock, they should either have their own handles + * or protect a single handle with a critical section. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * lockname - pointer to name of lock + * plock - pointer to returned handle for lock + * + * Returns: + * + * If successful, a handle for the specified lock is returned via + * 'plock', and the return value is zero. Otherwise the return + * value is a negative error code (see nslock.h), and an error + * frame is generated if an error frame list was provided. + */ + +NSAPI_PUBLIC int nsLockOpen(NSErr_t * errp, char * lockname, void **plock) +{ + NSLock_t * nl = 0; /* pointer to lock structure */ + int len; /* length of lockname */ + int eid; + int rv; + +#ifdef FILE_UNIX + /* Have we created the critical section for NSLock_List yet? */ + if (NSLock_Crit == 0) { + + /* Narrow the window for simultaneous initialization */ + NSLock_Crit = (CRITICAL)(-1); + + /* Create it */ + NSLock_Crit = crit_init(); + } + + /* Lock the list of locks */ + crit_enter(NSLock_Crit); + + /* See if a lock with the specified name exists already */ + for (nl = NSLock_List; nl != 0; nl = nl->nl_next) { + if (!strcmp(nl->nl_name, lockname)) break; + } + + /* Create a new lock if we didn't find it */ + if (nl == 0) { + + len = strlen(lockname); + + nl = (NSLock_t *)PERM_MALLOC(sizeof(NSLock_t) + len + 5); + if (nl == 0) goto err_nomem; + + nl->nl_name = (char *)(nl + 1); + strcpy(nl->nl_name, lockname); + strcpy(&nl->nl_name[len], ".lck"); + nl->nl_cnt = 0; + + nl->nl_fd = open(nl->nl_name, O_RDWR|O_CREAT|O_EXCL, 0644); + if (nl->nl_fd < 0) { + + if (errno != EEXIST) { + crit_exit(NSLock_Crit); + goto err_create; + } + + /* O_RDWR or O_WRONLY is required to use lockf on Solaris */ + nl->nl_fd = open(nl->nl_name, O_RDWR, 0); + if (nl->nl_fd < 0) { + crit_exit(NSLock_Crit); + goto err_open; + } + } + + /* Remove ".lck" from the lock name */ + nl->nl_name[len] = 0; + + /* Create a critical section for this lock (gag!) */ + nl->nl_crit = crit_init(); + + /* Add this lock to NSLock_List */ + nl->nl_next = NSLock_List; + NSLock_List = nl; + } + + crit_exit(NSLock_Crit); + +#else +/* write me */ + nl = (void *)4; +#endif /* FILE_UNIX */ + + *plock = (void *)nl; + return 0; + + err_nomem: + eid = NSLERR1000; + rv = NSLERRNOMEM; + nserrGenerate(errp, rv, eid, NSLock_Program, 0); + goto punt; + + err_create: + eid = NSLERR1020; + rv = NSLERRCREATE; + goto err_file; + + err_open: + eid = NSLERR1040; + rv = NSLERROPEN; + err_file: + nserrGenerate(errp, rv, eid, NSLock_Program, 1, nl->nl_name); + punt: + if (nl) { + FREE(nl); + } + *plock = 0; + return rv; +} + +/* + * Description (nsLockAcquire) + * + * This function is used to acquire exclusive ownership of a lock + * previously accessed via nsLockOpen(). The calling thread will + * be blocked until the lock is acquired. Other threads in the + * process should not be blocked. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * lock - handle for lock from nsLockOpen() + * + * Returns: + * + * If successful, the return value is zero. Otherwise the return + * value is a negative error code (see nslock.h), and an error + * frame is generated if an error frame list was provided. + */ + +NSAPI_PUBLIC int nsLockAcquire(NSErr_t * errp, void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + int eid; + int rv; + +#ifdef FILE_UNIX + /* Enter the critical section for the lock */ + crit_enter(nl->nl_crit); + + /* Acquire the file lock if we haven't already */ + if (nl->nl_cnt == 0) { + rv = system_flock(nl->nl_fd); + if (rv) { + crit_exit(nl->nl_crit); + goto err_lock; + } + } + + /* Bump the lock count */ + nl->nl_cnt++; + + crit_exit(nl->nl_crit); +#else + /* write me */ +#endif /* FILE_UNIX */ + + /* Indicate success */ + return 0; + + err_lock: + eid = NSLERR1100; + rv = NSLERRLOCK; + nserrGenerate(errp, rv, eid, NSLock_Program, 1, nl->nl_name); + + return rv; +} + +/* + * Description (nsLockRelease) + * + * This function is used to release exclusive ownership to a lock + * that was previously obtained via nsLockAcquire(). + * + * Arguments: + * + * lock - handle for lock from nsLockOpen() + */ + +NSAPI_PUBLIC void nsLockRelease(void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + +#ifdef FILE_UNIX + assert(nl->nl_cnt > 0); + + crit_enter(nl->nl_crit); + + if (--nl->nl_cnt <= 0) { + system_ulock(nl->nl_fd); + nl->nl_cnt = 0; + } + + crit_exit(nl->nl_crit); +#endif /* FILE_UNIX */ +} + +/* + * Description (nsLockClose) + * + * This function is used to close a lock handle that was previously + * acquired via nsLockOpen(). The lock should not be owned. + * + * Arguments: + * + * lock - handle for lock from nsLockOpen() + */ + +NSAPI_PUBLIC void nsLockClose(void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + +#ifdef FILE_UNIX + /* Don't do anything with the lock, since it will get used again */ +#if 0 + crit_enter(nl->nl_crit); + close(nl->nl_fd); + crit_exit(nl->nl_crit); + FREE(nl); +#endif +#else + /* write me */ +#endif FILE_UNIX +} diff --git a/lib/libaccess/nsumgmt.cpp b/lib/libaccess/nsumgmt.cpp new file mode 100644 index 00000000..3db677f0 --- /dev/null +++ b/lib/libaccess/nsumgmt.cpp @@ -0,0 +1,456 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsumgmt.c) + * + * This module contains routines for managing information in a + * Netscape user database. Information for a particular user + * is modified by retrieving the current information in the form + * of a user object (UserObj_t), calling functions in this module, + * to modify the user object, and then calling userStore() to + * write the information in the user object back to the database. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/nsdbmgmt.h" +#define __PRIVATE_NSUSER +#include "libaccess/nsumgmt.h" + +/* + * Description (userAddGroup) + * + * This function adds a group id to the list of group ids associated + * with a user object. + * + * Arguments: + * + * uoptr - user object pointer + * gid - group id to be added + * + * Returns: + * + * Returns zero if the group id is already present in the group id list. + * Returns one if the group id was added successfully. + * Returns a negative value if an error occurs. + */ + +int userAddGroup(UserObj_t * uoptr, USI_t gid) +{ + int rv; + + rv = usiInsert(&uoptr->uo_groups, gid); + + if (rv > 0) { + + uoptr->uo_flags |= UOF_MODIFIED; + } + + return rv; +} + +/* + * Description (userCreate) + * + * This function creates a user object, using information about + * the user provided by the caller. The strings passed for the + * user account name, password, and real user name may be on the + * stack. The user id is set to zero, but the user object is + * marked as being new. A user id will be assigned when + * userStore() is called to add the user to a user database. + * + * Arguments: + * + * name - pointer to user account name string + * pwd - pointer to (encrypted) password string + * rname - real user name (gecos string) + * + * Returns: + * + * A pointer to a dynamically allocated UserObj_t structure is + * returned. + */ + +NSAPI_PUBLIC UserObj_t * userCreate(NTS_t name, NTS_t pwd, NTS_t rname) +{ + UserObj_t * uoptr; /* user object pointer */ + + uoptr = (UserObj_t *)MALLOC(sizeof(UserObj_t)); + if (uoptr) { + uoptr->uo_name = (NTS_t)STRDUP((char *)name); + uoptr->uo_pwd = (pwd) ? (NTS_t)STRDUP((char *)pwd) : 0; + uoptr->uo_uid = 0; + uoptr->uo_flags = (UOF_MODIFIED | UOF_NEW); + uoptr->uo_rname = (rname) ? (NTS_t)STRDUP((char *)rname) : 0; + UILINIT(&uoptr->uo_groups); + } + + return uoptr; +} + +/* + * Description (userDeleteGroup) + * + * This function removes a specified group id from a user object's + * list of groups. + * + * Arguments: + * + * uoptr - pointer to user object + * gid - group id to remove + * + * Returns: + * + * The return value is zero if the specified group id was not present + * in the user object, or one if the group was successfully removed. + */ + +int userDeleteGroup(UserObj_t * uoptr, USI_t gid) +{ + int rv; /* return value */ + + rv = usiRemove(&uoptr->uo_groups, gid); + if (rv > 0) { + uoptr->uo_flags |= UOF_MODIFIED; + } + + return rv; +} + +/* + * Description (userEncode) + * + * This function encodes a user object into a user DB record. + * + * Arguments: + * + * uoptr - pointer to user object + * ureclen - pointer to returned record length + * urecptr - pointer to returned record pointer + * + * Returns: + * + * The function return value is zero if successful. The length + * and location of the created attribute record are returned + * through 'ureclen' and 'urecptr'. A non-zero function value + * is returned if there's an error. + */ + +int userEncode(UserObj_t * uoptr, int * ureclen, ATR_t * urecptr) +{ + int reclen; /* length of DB record */ + ATR_t rptr; /* DB record pointer */ + ATR_t rstart = 0; /* pointer to beginning of DB record */ + ATR_t glptr; /* saved pointer to UAT_GROUPS length */ + ATR_t gptr; /* saved pointer to after length at glptr */ + int pwdlen; /* password encoding length */ + int uidlen; /* uid encoding length */ + int fllen; /* account flags encoding length */ + USI_t rnlen; /* real name encoding length */ + USI_t nglen; /* group count encoding length */ + USI_t gcnt; /* number of group ids */ + USI_t * gids; /* pointer to array of group ids */ + int i; /* group id index */ + int rv = -1; + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* UAT_PASSWORD attribute: tag, length, NTS */ + pwdlen = NTSLENGTH(uoptr->uo_pwd); + reclen = 1 + 1 + pwdlen; + if (pwdlen > 127) goto punt; + + /* UAT_UID attribute: tag, length, USI */ + uidlen = USILENGTH(uoptr->uo_uid); + reclen += (1 + 1 + uidlen); + + /* UAT_ACCFLAGS attribute: tag, length, USI */ + fllen = USILENGTH(uoptr->uo_flags & UOF_DBFLAGS); + reclen += (1 + 1 + fllen); + + /* UAT_REALNAME attribute: tag, length, NTS */ + rnlen = NTSLENGTH(uoptr->uo_rname); + reclen += (1 + USILENGTH(rnlen) + rnlen); + + /* UAT_GROUPS attribute: tag, length, USI(count), USI(gid)... */ + gcnt = UILCOUNT(&uoptr->uo_groups); + nglen = USILENGTH(gcnt); + reclen += (1 + USIALLOC() + nglen + (5 * gcnt)); + + /* Allocate the attribute record buffer */ + rptr = (ATR_t)MALLOC(reclen); + if (rptr) { + + /* Save pointer to start of record */ + rstart = rptr; + + /* Encode UAT_PASSWORD attribute */ + *rptr++ = UAT_PASSWORD; + *rptr++ = pwdlen; + rptr = NTSENCODE(rptr, uoptr->uo_pwd); + + /* Encode UAT_UID attribute */ + *rptr++ = UAT_UID; + *rptr++ = uidlen; + rptr = USIENCODE(rptr, uoptr->uo_uid); + + /* Encode UAT_ACCFLAGS attribute */ + *rptr++ = UAT_ACCFLAGS; + *rptr++ = fllen; + rptr = USIENCODE(rptr, (uoptr->uo_flags & UOF_DBFLAGS)); + + /* Encode UAT_REALNAME attribute */ + *rptr++ = UAT_REALNAME; + rptr = USIENCODE(rptr, rnlen); + rptr = NTSENCODE(rptr, uoptr->uo_rname); + + /* Encode UAT_GROUPS attribute */ + *rptr++ = UAT_GROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + rptr = USIENCODE(rptr, gcnt); + + /* Generate group ids encodings */ + gids = UILLIST(&uoptr->uo_groups); + for (i = 0; i < gcnt; ++i) { + rptr = USIENCODE(rptr, gids[i]); + } + + /* Now fix up the UAT_GROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Return record length and location if requested */ + if (ureclen) *ureclen = rptr - rstart; + if (urecptr) *urecptr = rstart; + + /* Indicate success */ + rv = 0; + } + + punt: + return rv; +} + +/* + * Description (userRemove) + * + * This function is called to remove a user from a specified user + * database. Both the primary DB file and the id-to-name DB file + * are updated. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - (unused - must be zero) + * name - pointer to user account name + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int userRemove(NSErr_t * errp, void * userdb, int flags, NTS_t name) +{ + UserObj_t * uoptr; /* user object pointer */ + int rv; + int rv2; + + /* First retrieve the user record */ + uoptr = userFindByName(errp, userdb, name); + if (!uoptr) { + /* Error - specified user not found */ + return NSAERRNAME; + } + + /* Free the user id value, if any */ + rv = 0; + if (uoptr->uo_uid != 0) { + rv = ndbFreeId(errp, userdb, 0, (char *)name, uoptr->uo_uid); + } + + rv2 = ndbDeleteName(errp, userdb, 0, 0, (char *)name); + + return (rv) ? rv : rv2; +} + +/* + * Description (userRename) + * + * This function is called to change the account name associated + * with an existing user. The caller provides a pointer to a + * user object for the existing user (with the current user account + * name referenced by uo_name), and the new account name for this + * user. A check is made to ensure the uniqueness of the new name + * in the specified user database. The account name in the user + * object is modified. The user database is not modified until + * userStore() is called. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * uoptr - user object pointer + * newname - pointer to new account name string + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The user object remains intact in either + * case. + */ + +NSAPI_PUBLIC int userRename(NSErr_t * errp, void * userdb, UserObj_t * uoptr, NTS_t newname) +{ + int reclen; /* user record length */ + ATR_t recptr = 0; /* user record pointer */ + char * oldname; /* old user account name */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Save the current account name and replace it with the new one */ + oldname = (char *)uoptr->uo_name; + uoptr->uo_name = (unsigned char *) STRDUP((char *)newname); + + if ((oldname != 0) && !(uoptr->uo_flags & UOF_NEW)) { + + /* Convert the information in the user object to a DB record */ + rv = userEncode(uoptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* + * Store the record in the database + * under the new user account name. + */ + rv = ndbStoreName(errp, userdb, NDBF_NEWNAME, + 0, (char *)uoptr->uo_name, reclen, (char *)recptr); + if (rv) goto punt; + + /* Change the mapping of the user id to the new name */ + rv = ndbRenameId(errp, userdb, 0, (char *)uoptr->uo_name, uoptr->uo_uid); + if (rv) goto punt; + + /* Delete the user record with the old account name */ + rv = ndbDeleteName(errp, userdb, 0, 0, oldname); + if (rv) goto punt; + } + else { + /* Set flags in user object for userStore() */ + uoptr->uo_flags |= UOF_MODIFIED; + } + + punt: + if (recptr) { + FREE(recptr); + } + if (oldname) { + FREE(oldname); + } + return rv; + + err_nomem: + eid = NSAUERR1000; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; +} + +/* + * Description (userStore) + * + * This function is called to store a user object in the database. + * If the object was created by userCreate(), it is assumed to be + * a new user account, the user account name must not match any + * existing user account names in the database, and a uid is + * assigned before adding the user to the database. If the object + * was created by userFindByName(), the information in the user + * object will replace the existing database entry for the + * indicated user account name. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - (unused - must be zero) + * uoptr - user object pointer + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The user object remains intact in either + * case. + */ + +NSAPI_PUBLIC int userStore(NSErr_t * errp, void * userdb, int flags, UserObj_t * uoptr) +{ + ATR_t recptr = 0; + USI_t uid; + int reclen = 0; + int stflags = 0; + int eid; + int rv; + + /* If this is a new user, allocate a uid value */ + if (uoptr->uo_flags & UOF_NEW) { + /* + * Yes, allocate a user id and add a user id to user + * account name mapping to the id-to-name DB file. + */ + uid = 0; + rv = ndbAllocId(errp, userdb, 0, (char *)uoptr->uo_name, &uid); + if (rv) goto punt; + + uoptr->uo_uid = uid; + + /* Let the database manager know that this is a new entry */ + stflags = NDBF_NEWNAME; + } + + /* Convert the information in the user object to a DB record */ + rv = userEncode(uoptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* Store the record in the database under the user account name. */ + rv = ndbStoreName(errp, userdb, stflags, + 0, (char *)uoptr->uo_name, reclen, (char *)recptr); + if (rv) goto punt; + + FREE(recptr); + recptr = 0; + + uoptr->uo_flags &= ~(UOF_NEW | UOF_MODIFIED); + return 0; + + err_nomem: + eid = NSAUERR1100; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + + punt: + if (recptr) { + FREE(recptr); + } + if ((uoptr->uo_flags & UOF_NEW) && (uid != 0)) { + /* Free the user id value if we failed after allocating it */ + ndbFreeId(errp, userdb, 0, (char *)uoptr->uo_name, uid); + } + return rv; +} diff --git a/lib/libaccess/nsuser.cpp b/lib/libaccess/nsuser.cpp new file mode 100644 index 00000000..158235a5 --- /dev/null +++ b/lib/libaccess/nsuser.cpp @@ -0,0 +1,309 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsuser.c) + * + * This module contains routines for accessing information in a + * Netscape user database. User information is returned in the + * form of a user object (UserObj_t), defined in nsauth.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_NSUSER +#include "libaccess/nsuser.h" + +/* Authentication facility name for error frame generation */ +char * NSAuth_Program = "NSAUTH"; + +/* + * Description (userDecode) + * + * This function decodes an external user DB record into a dynamically + * allocated UserObj_t structure. The DB record is encoded as an + * attribute record as defined in attrec.h. + * + * Arguments: + * + * name - pointer to user account name string + * ureclen - length of the user DB record, in octets + * urecptr - pointer to user DB record + * + * Returns: + * + * A pointer to the allocated UserObj_t structure is returned. + */ + +UserObj_t * userDecode(NTS_t name, int ureclen, ATR_t urecptr) +{ + ATR_t cp = urecptr; /* current pointer into DB record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + USI_t gcnt; /* number of group ids */ + USI_t * gids; /* pointer to array of group ids */ + int i; /* group id index */ + UserObj_t * uoptr; /* user object pointer */ + + /* Allocate a user object structure */ + uoptr = (UserObj_t *)MALLOC(sizeof(UserObj_t)); + if (uoptr) { + + uoptr->uo_name = (unsigned char *) STRDUP((char *)name); + uoptr->uo_pwd = 0; + uoptr->uo_uid = 0; + uoptr->uo_flags = 0; + uoptr->uo_rname = 0; + UILINIT(&uoptr->uo_groups); + + /* Parse user DB record */ + while ((cp - urecptr) < ureclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case UAT_PASSWORD: /* encrypted password */ + cp = NTSDECODE(cp, &uoptr->uo_pwd); + break; + + case UAT_UID: /* user id */ + cp = USIDECODE(cp, &uoptr->uo_uid); + break; + + case UAT_ACCFLAGS: /* account flags */ + cp = USIDECODE(cp, &uoptr->uo_flags); + break; + + case UAT_REALNAME: /* real name of user */ + cp = NTSDECODE(cp, &uoptr->uo_rname); + break; + + case UAT_GROUPS: /* groups which include user */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, &gcnt); + + if (gcnt > 0) { + + /* Allocate space for group ids */ + gids = usiAlloc(&uoptr->uo_groups, gcnt); + if (gids) { + for (i = 0; i < gcnt; ++i) { + cp = USIDECODE(cp, gids + i); + } + } + } + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + } + + return uoptr; +} + +/* + * Description (userEnumHelp) + * + * This is a local function that is called by NSDB during user + * database enumeration. It decodes user records into user + * objects, and presents them to the caller of userEnumerate(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to UserEnumArgs_t structure + * namelen - user record key length including null + * terminator + * name - user record key (user account name) + * reclen - length of user record + * recptr - pointer to user record contents + * + * Returns: + * + * Returns whatever value is returned from the upcall to the caller + * of userEnumerate(). + */ + +static int userEnumHelp(NSErr_t * errp, void * parg, + int namelen, char * name, int reclen, char * recptr) +{ + UserEnumArgs_t * ue = (UserEnumArgs_t *)parg; + UserObj_t * uoptr; /* user object pointer */ + int rv; + + uoptr = userDecode((NTS_t)name, reclen, (ATR_t)recptr); + + rv = (*ue->func)(errp, ue->user, uoptr); + + if (!(ue->flags & UOF_ENUMKEEP)) { + userFree(uoptr); + } + + return rv; +} + +/* + * Description (userEnumerate) + * + * This function enumerates all of the users in a specified user + * database, calling a caller-specified function with a user object + * for each user in the database. A 'flags' value of UOF_ENUMKEEP + * can be specified to keep the user objects around (not free them) + * after the caller's function returns. Otherwise, each user + * object is freed after being presented to the caller's function. + * The 'argp' argument is an opaque pointer, which is passed to + * the caller's function as 'parg' on each call, along with a + * user object pointer. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - bit flags: + * UOF_ENUMKEEP - keep user objects + * argp - passed to 'func' as 'parg' + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code, and an error frame is generated if an error + * frame list was provided by the caller. + */ + +int userEnumerate(NSErr_t * errp, void * userdb, int flags, void * argp, + int (*func)(NSErr_t * ferrp, void * parg, UserObj_t * uoptr)) +{ + int rv; + UserEnumArgs_t args; + + args.userdb = userdb; + args.flags = flags; + args.func = func; + args.user = argp; + + rv = ndbEnumerate(errp, + userdb, NDBF_ENUMNORM, (void *)&args, userEnumHelp); + + return rv; +} + +/* + * Description (userFindByName) + * + * This function looks up a user record for a specified user account + * name, converts the user record to the internal user object form, + * and returns a pointer to the user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * name - user account name to find + * + * Returns: + * + * If successful, the return value is a pointer to a user object + * for the specified user. Otherwise it is 0, and an error frame + * is generated if an error frame list was provided by the caller. + */ + +UserObj_t * userFindByName(NSErr_t * errp, void * userdb, NTS_t name) +{ + UserObj_t * uoptr = 0; + ATR_t urecptr; + int ureclen; + int rv; + + /* Look up the user name in the database */ + rv = ndbFindName(errp, userdb, 0, (char *) name, &ureclen, (char **)&urecptr); + if (rv == 0) { + + /* Got the user record. Decode into a user object. */ + uoptr = userDecode(name, ureclen, urecptr); + } + + return uoptr; +} + +/* + * Description (userFindByUid) + * + * This function looks up a user record for a specified user id, + * converts the user record to the internal user object form, and + * returns a pointer to the user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * uid - user id to find + * + * Returns: + * + * If successful, the return value is a pointer to a user object + * for the specified user. Otherwise it is 0, and an error frame + * is generated if an error frame list was provided by the caller. + */ + +UserObj_t * userFindByUid(NSErr_t * errp, void * userdb, USI_t uid) +{ + UserObj_t * uoptr = 0; + NTS_t name; + ATR_t urecptr; + int ureclen; + int rv; + + /* Get the user account name corresponding to the uid */ + rv = ndbIdToName(errp, userdb, uid, 0, (char **)&name); + if (rv == 0) { + + rv = ndbFindName(errp, userdb, 0, (char *)name, &ureclen, (char **)&urecptr); + if (rv == 0) { + + /* Got the user record. Decode into a user object. */ + uoptr = userDecode(name, ureclen, urecptr); + } + } + + return uoptr; +} + +/* + * Description (userFree) + * + * This function is called to free a user object. User objects + * are not automatically freed when a user database is closed. + * + * Arguments: + * + * uoptr - user object pointer + * + */ + +NSAPI_PUBLIC void userFree(UserObj_t * uoptr) +{ + if (uoptr) { + + if (uoptr->uo_name) FREE(uoptr->uo_name); + if (uoptr->uo_pwd) FREE(uoptr->uo_pwd); + if (uoptr->uo_rname) FREE(uoptr->uo_rname); + UILFREE(&uoptr->uo_groups); + FREE(uoptr); + } +} diff --git a/lib/libaccess/oneeval.cpp b/lib/libaccess/oneeval.cpp new file mode 100644 index 00000000..be837599 --- /dev/null +++ b/lib/libaccess/oneeval.cpp @@ -0,0 +1,1054 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (acleval.c) + * + * This module provides functions for evaluating Access Control List + * (ACL) structures in memory. + * + */ + +#include <string.h> +#include <sys/types.h> +#include <assert.h> + +#include <netsite.h> +#include <base/systems.h> +#include <base/crit.h> +#include <base/session.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <libaccess/symbols.h> +#include <libaccess/aclerror.h> +#include <libaccess/aclglobal.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "access_plhash.h" +#include "aclutil.h" +#include "aclcache.h" +#include "oneeval.h" +#include "permhash.h" + +static int acl_default_result = ACL_RES_DENY; + +static ACLDispatchVector_t __nsacl_vector = { + + /* Error frame stack support */ + + nserrDispose, + nserrFAlloc, + nserrFFree, + nserrGenerate, + + /* Property list support */ + + PListAssignValue, + PListCreate, + PListDefProp, + PListDeleteProp, + PListFindValue, + PListInitProp, + PListNew, + PListDestroy, + PListGetValue, + PListNameProp, + PListSetType, + PListSetValue, + PListEnumerate, + PListDuplicate, + PListGetPool, + + /* ACL attribute handling */ + + ACL_LasRegister, + + /* method/dbtype registration routines */ + + ACL_MethodRegister, + ACL_MethodIsEqual, + ACL_MethodNameIsEqual, + ACL_MethodFind, + ACL_MethodGetDefault, + ACL_MethodSetDefault, + ACL_AuthInfoGetMethod, + + ACL_DbTypeRegister, + ACL_DbTypeIsEqual, + ACL_DbTypeNameIsEqual, + ACL_DbTypeFind, + ACL_DbTypeGetDefault, + ACL_AuthInfoGetDbType, + ACL_DbTypeIsRegistered, + ACL_DbTypeParseFn, + + ACL_AttrGetterRegister, + + ACL_ModuleRegister, + ACL_GetAttribute, + ACL_DatabaseRegister, + ACL_DatabaseFind, + ACL_DatabaseSetDefault, + ACL_LDAPDatabaseHandle, + ACL_AuthInfoGetDbname, + ACL_CacheFlushRegister, + ACL_CacheFlush, + + /* ACL language and file interfaces */ + + ACL_ParseFile, + ACL_ParseString, + ACL_WriteString, + ACL_WriteFile, + ACL_FileRenameAcl, + ACL_FileDeleteAcl, + ACL_FileGetAcl, + ACL_FileSetAcl, + + /* ACL Expression construction interfaces */ + + ACL_ExprNew, + ACL_ExprDestroy, + ACL_ExprSetPFlags, + ACL_ExprClearPFlags, + ACL_ExprTerm, + ACL_ExprNot, + ACL_ExprAnd, + ACL_ExprOr, + ACL_ExprAddAuthInfo, + ACL_ExprAddArg, + ACL_ExprSetDenyWith, + ACL_ExprGetDenyWith, + ACL_ExprAppend, + + /* ACL manipulation */ + + ACL_AclNew, + ACL_AclDestroy, + + /* ACL list manipulation */ + + ACL_ListNew, + ACL_ListConcat, + ACL_ListAppend, + ACL_ListDestroy, + ACL_ListFind, + ACL_ListAclDelete, + ACL_ListGetNameList, + ACL_NameListDestroy, + + /* ACL evaluation */ + + ACL_EvalTestRights, + ACL_EvalNew, + ACL_EvalDestroy, + ACL_EvalSetACL, + ACL_EvalGetSubject, + ACL_EvalSetSubject, + ACL_EvalGetResource, + ACL_EvalSetResource, + + /* Access to critical section for ACL cache */ + + ACL_CritEnter, + ACL_CritExit, + + /* Miscellaneous functions */ + + ACL_AclGetTag, + ACL_ListGetFirst, + ACL_ListGetNext, + + /* Functions added after ES 3.0 release */ + ACL_DatabaseGetDefault, + ACL_SetDefaultResult, + ACL_GetDefaultResult +}; + +NSAPI_PUBLIC ACLDispatchVector_t *__nsacl_table = &__nsacl_vector; + +int ACLEvalAce( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + ACLExprHandle_t *ace, + ACLCachable_t *cachable, + PList_t autharray[], + PList_t global_auth + ) +{ + ACLCachable_t local_cachable; + int result; + ACLExprEntry_t *expr; + int expr_index = 0; + + expr = &ace->expr_arry[0]; + *cachable = ACL_INDEF_CACHABLE; + + while (TRUE) + { + local_cachable = ACL_NOT_CACHABLE; + + /* Call the LAS driver */ + if (!expr->las_eval_func) { + ACL_CritEnter(); + if (!expr->las_eval_func) { /* Must check again after locking */ + ACL_LasFindEval(errp, expr->attr_name, &expr->las_eval_func); + if (!expr->las_eval_func) { /* Couldn't find it */ + ACL_CritExit(); + return LAS_EVAL_INVALID; + } + } + ACL_CritExit(); + } + result = (*expr->las_eval_func)( + errp, + expr->attr_name, + expr->comparator, + expr->attr_pattern, + &local_cachable, + &expr->las_cookie, + acleval->subject, + acleval->resource, + autharray ? autharray[expr_index] : NULL, + global_auth); + + /* Evaluate the cachable value */ + if (local_cachable < *cachable) { + + /* Take the minimum value */ + *cachable = local_cachable; + } + + /* Evaluate the return code */ + switch (result) { + case LAS_EVAL_TRUE: + if (expr->true_idx < 0) + return (expr->true_idx); + else { + expr_index = expr->true_idx; + expr = &ace->expr_arry[expr->true_idx]; + } + break; + + case LAS_EVAL_FALSE: + if (expr->false_idx < 0) + return (expr->false_idx); + else { + expr_index = expr->false_idx; + expr = &ace->expr_arry[expr->false_idx]; + } + break; + + default: + return (result); + } + + } +} + + +int +ACL_EvalDestroyContext(ACLListCache_t *cache) +{ + ACLAceEntry_t *cur_ace, *next_ace; + ACLAceNumEntry_t *cur_num_p, *next_num_p; + ACLExprHandle_t *acep; + + if (!cache) + return 0; + + PR_HashTableDestroy(cache->Table); + cache->Table = NULL; + + cur_ace = cache->acelist; + cache->acelist = NULL; + while (cur_ace) { + if (cur_ace->autharray) + PERM_FREE(cur_ace->autharray); + if ((cur_ace->global_auth) && + (cur_ace->acep->expr_type == ACL_EXPR_TYPE_AUTH)) + PListDestroy(cur_ace->global_auth); + next_ace = cur_ace->next; + acep = cur_ace->acep; /* The ACE structure itself */ + PERM_FREE(cur_ace); + cur_ace = next_ace; + } + + cur_num_p = cache->chain_head; + cache->chain_head = NULL; + while (cur_num_p) { + next_num_p = cur_num_p->chain; + PERM_FREE(cur_num_p); + cur_num_p = next_num_p; + } + + PERM_FREE(cache); + + return 0; +} + + +/* ACLEvalBuildContext + * Builds three structures: + * Table - A hash table of all access rights referenced by any ACE in any + * of the ACLs in this list. Each hash entry then has a list of + * the relevant ACEs, in the form of indexes to the ACE linked + * list. + * ACE List - A linked list of all the ACEs in the proper evaluation order. + * + * For concurrency control, the caller must call ACL_CritEnter() + */ +int +ACLEvalBuildContext( + NSErr_t *errp, + ACLEvalHandle_t *acleval) +{ + ACLHandle_t *acl; + ACLExprHandle_t *ace; + int ace_cnt = -1; + ACLAceEntry_t *acelast, *new_ace; + ACLAceNumEntry_t *entry, *temp_entry; + char **argp; + ACLListCache_t *cache; + ACLWrapper_t *wrapper; + PList_t curauthplist=NULL, absauthplist=NULL; + int i, rv; + ACLExprEntry_t *expr; + PList_t authplist; + + /* Allocate the cache context and link it into the ACLListHandle */ + cache = (ACLListCache_t *)PERM_CALLOC(sizeof(ACLListCache_t)); + if (cache == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4010, ACL_Program, 0); + goto error; + } + + /* Allocate the access rights hash table */ + cache->Table = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + + if (cache->Table == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4000, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToCreateHash)); + goto error; + } + + wrapper = acleval->acllist->acl_list_head; + + /* Loop through all the ACLs in the list */ + while (wrapper) + { + acl = wrapper->acl; + ace = acl->expr_list_head; + + while (ace) /* Loop through all the ACEs in this ACL */ + { + + /* allocate a new ace list entry and link it in to the ordered + * list. + */ + new_ace = (ACLAceEntry_t *)PERM_CALLOC(sizeof(ACLAceEntry_t)); + if (new_ace == (ACLAceEntry_t *)NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4020, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAceEntry)); + goto error; + } + new_ace->acep = ace; + ace_cnt++; + + if (cache->acelist == NULL) + cache->acelist = acelast = new_ace; + else { + acelast->next = new_ace; + acelast = new_ace; + new_ace->acep = ace; + } + new_ace->next = NULL; + + argp = ace->expr_argv; + + switch (ace->expr_type) + { + case ACL_EXPR_TYPE_ALLOW: + case ACL_EXPR_TYPE_DENY: + + /* Add this ACE to the appropriate entries in the access rights + * hash table + */ + while (*argp) + { + entry = + (ACLAceNumEntry_t *)PERM_CALLOC(sizeof(ACLAceNumEntry_t)); + if (entry == (ACLAceNumEntry_t *)NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4030, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAceEntry)); + goto error; + } + if (cache->chain_head == NULL) + cache->chain_head = cache->chain_tail = entry; + else { + cache->chain_tail->chain = entry; + cache->chain_tail = entry; + } + entry->acenum = ace_cnt; + + /* + * OK to call PL_HasTableLookup() even though it mods + * the Table as this routine is called in critical section. + */ + temp_entry = (ACLAceNumEntry_t *)PL_HashTableLookup(cache->Table, *argp); + /* the first ACE for this right? */ + if (temp_entry) { + /* Link it in at the end */ + while (temp_entry->next) { + temp_entry = temp_entry->next; + } + temp_entry->next = entry; + } else /* just link it in */ + PR_HashTableAdd(cache->Table, *argp, entry); + + argp++; + + } + + /* See if any of the clauses require authentication. */ + if (curauthplist) { + for (i = 0; i < ace->expr_term_index; i++) { + expr = &ace->expr_arry[i]; + rv = PListFindValue(curauthplist, expr->attr_name, + NULL, &authplist); + if (rv > 0) { + /* First one for this ACE? */ + if (!new_ace->autharray) { + new_ace->autharray = (PList_t *)PERM_CALLOC(sizeof(PList_t *) * ace->expr_term_index); + if (!new_ace->autharray) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4040, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPointerArray)); + goto error; + } + } + new_ace->autharray[i] = authplist; + } + } + } + break; + + case ACL_EXPR_TYPE_AUTH: + + /* Allocate the running auth tables if none yet */ + if (!curauthplist) { + curauthplist = PListNew(NULL); + if (!curauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + absauthplist = PListNew(NULL); + if (!absauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + } else { /* duplicate the existing auth table */ + curauthplist = PListDuplicate(curauthplist, NULL, 0); + if (!curauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + } + + /* For each listed attribute */ + while (*argp) + { + /* skip any attributes that were absoluted */ + if (PListFindValue(absauthplist, *argp, NULL, NULL) < 0) + { + /* Save pointer to the property list */ + PListInitProp(curauthplist, NULL, *argp, ace->expr_auth, + ace->expr_auth); + if (IS_ABSOLUTE(ace->expr_flags)) + PListInitProp(absauthplist, NULL, *argp, NULL, + NULL); + } + + argp++; + } + + break; + + case ACL_EXPR_TYPE_RESPONSE: + (void) ACL_ExprGetDenyWith(NULL, ace, &cache->deny_type, + &cache->deny_response); + break; + + default: + NS_ASSERT(0); + + } /* switch expr_type */ + + new_ace->global_auth = curauthplist; + ace = ace->expr_next; + } + + /* Next ACL please */ + wrapper = wrapper->wrap_next; + } + + if (absauthplist) + PListDestroy(absauthplist); + + /* This must be done last to avoid a race in initialization */ + acleval->acllist->cache = (void *)cache; + + return 0; + +error: + if (absauthplist) + PListDestroy(absauthplist); + if (cache) { + ACL_EvalDestroyContext(cache); + } + acleval->acllist->cache = NULL; + return ACL_RES_ERROR; +} + +/* ACL_InvalidateSubjectPList + * Given a new authentication plist, enumerate the plist and for each + * key in the plist, search for the matching key in the subject plist + * and delete any matches. E.g. "user", "group". + */ +void +ACL_InvalidateSubjectPList(char *attr, const void *value, void *user_data) +{ + PList_t subject = (PList_t)user_data; + + PListDeleteProp(subject, 0, attr); + return; +} + +NSAPI_PUBLIC int ACL_SetDefaultResult (NSErr_t *errp, + ACLEvalHandle_t *acleval, + int result) +{ + int rv; + + switch(result) { + case ACL_RES_ALLOW: + case ACL_RES_DENY: + case ACL_RES_FAIL: + case ACL_RES_INVALID: + acleval->default_result = result; + rv = 0; + break; + default: + rv = -1; + } + + return rv; +} + +NSAPI_PUBLIC int ACL_GetDefaultResult (ACLEvalHandle_t *acleval) +{ + return acleval->default_result; +} + +/* ACL_INTEvalTestRights + * INPUT + * *errp The usual error context stack + * *acleval A list of ACLs + * **rights An array of strings listing the requested rights + * **map_generic An array of strings listing the specific rights + * that map from the generic rights. + * OUTPUT + * **deny_type bong file type passed on the way back out + * **deny_response bong file pathname passed on the way back out + * **acl_tag Name of the ACL that denies access + * *expr_num ACE number within the denying ACL + * *cachable Is the result cachable? + */ +static int +ACL_INTEvalTestRights( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + char **rights, + char **map_generic, + char **deny_type, + char **deny_response, + char **acl_tag, + int *expr_num, + ACLCachable_t *cachable) +{ + struct rights_ent { + char right[64]; /* lowercase-ed rights string */ + int result; /* Interim result value */ + int absolute; /* ACE with absolute keyword */ + int count; /* # specific + generic rights */ + ACLAceNumEntry_t *acelist[ACL_MAX_GENERIC+1]; + /* List of relevant ACEs */ + }; + struct rights_ent *rarray_p; + struct rights_ent rights_arry[ACL_MAX_TEST_RIGHTS]; + ACLAceNumEntry_t *alllist; /* List of ACEs for "all" rights */ + ACLAceEntry_t *cur_ace; + ACLListCache_t *cache; + int rights_cnt = 0; + int prev_acenum, cur_acenum; + int i, j, right_num, delta; + ACLCachable_t ace_cachable; + int result; + int absolute; + int skipflag; + int g_num; /* index into the generic rights array. */ + char **g_rights; + PList_t global_auth=NULL; + int allow_error = 0; + int allow_absolute = 0; + char *allow_tag = NULL; + int allow_num = 0; + int default_result = ACL_GetDefaultResult(acleval); + + *acl_tag = NULL; + *expr_num = 0; + *cachable = ACL_INDEF_CACHABLE; + + /* + * The acleval contains the list of acis we are asking about. + * In our case it's always of length 1. + * The acleval is a per aclpb structure but + * the acllist is a global structure derived from the global + * aci cache--so access to acllist is multi-threaded. + * Hence, for example the use of the "read-only" hash + * lookup routines in this function--ACL_EvalTestRights() + * is called in a "reader only context" so this code is therefore + * thread-safe. + */ + + if (acleval->acllist == ACL_LIST_NO_ACLS) return ACL_RES_ALLOW; + + /* Build up the access right - indexed structures */ + if (acleval->acllist->cache == NULL) { + ACL_CritEnter(); + if (acleval->acllist->cache == NULL) { /* Check again */ + if (ACLEvalBuildContext(errp, acleval) == ACL_RES_ERROR) { + nserrGenerate(errp, ACLERRINTERNAL, ACLERR4110, ACL_Program, + 1, XP_GetAdminStr(DBT_EvalTestRightsEvalBuildContextFailed)); + ACL_CritExit(); + return ACL_RES_ERROR; + } + } + ACL_CritExit(); + } + cache = (ACLListCache_t *)acleval->acllist->cache; + *deny_response = cache->deny_response; + *deny_type = cache->deny_type; + + /* For the list of rights requested, get back the list of relevant + * ACEs. If we want + * to alter the precedence of allow/deny, this would be a good + * place to do it. + */ + + while (*rights) + { + rarray_p = &rights_arry[rights_cnt]; + + /* Initialize the rights array entry */ + strcpy(&rarray_p->right[0], *rights); + makelower(&rarray_p->right[0]); + rarray_p->result = default_result; + rarray_p->absolute = 0; + rarray_p->count = 1; // There's always the specific right + + /* Locate the list of ACEs that apply to the right */ + rarray_p->acelist[0] = + (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, rarray_p->right); + + /* See if the requested right also maps back to a generic right and + * if so, locate the acelist for it as well. + */ + if (map_generic) + { + for (g_rights=map_generic, g_num=0; *g_rights; g_rights++, g_num++) + { + if (strstr(*g_rights, rarray_p->right)) { + // Add it to our acelist, but skip 0 'cause that's the + // specific right. + rarray_p->acelist[rarray_p->count++] = + (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, + (char *)generic_rights[g_num]); + NS_ASSERT (rarray_p->count < ACL_MAX_GENERIC); + } + } + } + + rights_cnt++; + rights++; + NS_ASSERT (rights_cnt < ACL_MAX_TEST_RIGHTS); + } + + /* Special case - look for an entry that applies to "all" rights */ + alllist = (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, "all"); + + /* Ok, we've now got a list of relevant ACEs. Now evaluate things. */ + prev_acenum = -1; + cur_ace = cache->acelist; + + /* Loop through the relevant ACEs for the requested rights */ + while (TRUE) + { + cur_acenum = 10000; /* Pick a really high num so we lose */ + /* Find the lowest ACE among the rights lists */ + for (i=0; i<rights_cnt; i++) { + rarray_p = &rights_arry[i]; + if (rarray_p->absolute) continue; // This right doesn't matter + for (j=0; j<rarray_p->count; j++) { + if ((rarray_p->acelist[j] != NULL) && + (rarray_p->acelist[j]->acenum < cur_acenum)) { + cur_acenum = rarray_p->acelist[j]->acenum; + } + } + } + + /* Special case - look for the "all" rights ace list and see if its + * the lowest of all. + */ + if (alllist && (alllist->acenum < cur_acenum)) + cur_acenum = alllist->acenum; + + /* If no new ACEs then we're done - evaluate the rights list */ + if (cur_acenum == 10000) + break; + + /* Locate that ACE and evaluate it. We have to step through the + * linked list of ACEs to find it. + */ + if (prev_acenum == -1) + delta = cur_acenum; + else + delta = cur_acenum - prev_acenum; + + for (i=0; i<delta; i++) + cur_ace = cur_ace->next; + + if (global_auth && global_auth != cur_ace->global_auth) { + /* We must enumerate the auth_info plist and remove entries for + * each attribute from the subject property list. + */ + PListEnumerate(cur_ace->global_auth, ACL_InvalidateSubjectPList, + acleval->subject); + } + global_auth = cur_ace->global_auth; + + result = ACLEvalAce(errp, acleval, cur_ace->acep, &ace_cachable, + cur_ace->autharray, cur_ace->global_auth); + + /* Evaluate the cachable value */ + if (ace_cachable < *cachable) { + /* Take the minimum value */ + *cachable = ace_cachable; + } + + /* Under certain circumstances, no matter what happens later, + * the current result is not gonna change. + */ + if ((result != LAS_EVAL_TRUE) && (result != LAS_EVAL_FALSE)) { + if (cur_ace->acep->expr_type != ACL_EXPR_TYPE_ALLOW) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } else { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + return (EvalToRes(result)); + } + } else { + /* If the error is on an allow statement, continue processing + * and see if a subsequent allow works. If not, remember the + * error and return it. + */ + if (!allow_error) { + allow_error = EvalToRes(result); + allow_tag = cur_ace->acep->acl_tag; + allow_num = cur_ace->acep->expr_number; + } + if (IS_ABSOLUTE(cur_ace->acep->expr_flags)) { + allow_absolute = 1; + } + } + } + + /* Now apply the result to the rights array. Look to see which rights' + * acelist include the current one, or if the current one is on the + * "all" rights ace list. + */ + for (right_num=0; right_num<rights_cnt; right_num++) + { + rarray_p = &rights_arry[right_num]; + + /* Have we fixated on a prior result? */ + if (rarray_p->absolute) + continue; + + skipflag = 1; + + // Did this ace apply to this right? + for (i=0; i<rarray_p->count; i++) { + if ((rarray_p->acelist[i]) && + (rarray_p->acelist[i]->acenum == cur_acenum)) { + rarray_p->acelist[i] = rarray_p->acelist[i]->next; + skipflag = 0; + } + } + + /* This ace was on the "all" rights queue */ + if ((alllist) && (alllist->acenum == cur_acenum)) { + skipflag = 0; + } + + if (skipflag) + continue; /* doesn't apply to this right */ + + if (IS_ABSOLUTE(cur_ace->acep->expr_flags) && (result == + LAS_EVAL_TRUE)) { + rarray_p->absolute = 1; + absolute = 1; + } else + absolute = 0; + + switch (cur_ace->acep->expr_type) { + case ACL_EXPR_TYPE_ALLOW: + if (result == LAS_EVAL_TRUE) { + rarray_p->result = ACL_RES_ALLOW; + if (!allow_absolute) { + /* A previous ALLOW error was superceded */ + allow_error = 0; + } + } + else if (!*acl_tag) { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + } + break; + case ACL_EXPR_TYPE_DENY: + if (result == LAS_EVAL_TRUE) { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + if (absolute) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } + return (ACL_RES_DENY); + } + rarray_p->result = ACL_RES_DENY; + } + break; + default: + /* a non-authorization ACE, just ignore */ + break; + } + + } + + /* This ace was on the "all" rights queue */ + if ((alllist) && (alllist->acenum == cur_acenum)) { + alllist = alllist->next; + } + + /* If this is an absolute, check to see if all the rights + * have already been fixed by this or previous absolute + * statements. If so, we can compute the response without + * evaluating any more of the ACL list. + */ + if (absolute) { + for (i=0; i<rights_cnt; i++) { + /* Non absolute right, so skip this section */ + if (rights_arry[i].absolute == 0) + break; + /* This shouldn't be possible, but check anyway. + * Any absolute non-allow result should already + * have been returned earlier. + */ + if (rights_arry[i].result != ACL_RES_ALLOW) { + char result_str[16]; + sprintf(result_str, "%d", rights_arry[i].result); + nserrGenerate(errp, ACLERRINTERNAL, ACLERR4100, ACL_Program, 3, XP_GetAdminStr(DBT_EvalTestRightsInterimAbsoluteNonAllowValue), rights[i], result_str); + break; + } + if (i == (rights_cnt - 1)) + return ACL_RES_ALLOW; + } + } + + prev_acenum = cur_acenum; + + } /* Next ACE */ + + /* Do an AND on the results for the individual rights */ + for (right_num=0; right_num<rights_cnt; right_num++) + if (rights_arry[right_num].result != ACL_RES_ALLOW) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } + return (rights_arry[right_num].result); + } + + return (ACL_RES_ALLOW); + +} + + +/* ACL_CachableAclList + * Returns 1 if the ACL list will always evaluate to ALLOW for http_get. + */ +NSAPI_PUBLIC int +ACL_CachableAclList(ACLListHandle_t *acllist) +{ + ACLEvalHandle_t *acleval; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + int rv; + static char *rights[] = { "http_get", NULL }; + ACLCachable_t cachable=ACL_INDEF_CACHABLE; + + if (!acllist || acllist == ACL_LIST_NO_ACLS) { + return 1; + } + acleval = ACL_EvalNew(NULL, NULL); + ACL_EvalSetACL(NULL, acleval, acllist); + rv = ACL_INTEvalTestRights(NULL, acleval, rights, http_generic, + &bong_type, &bong, &acl_tag, &expr_num, + &cachable); + + ACL_EvalDestroyNoDecrement(NULL, NULL, acleval); + if (rv == ACL_RES_ALLOW && cachable == ACL_INDEF_CACHABLE) { + return 1; + } + + return 0; +} + + +NSAPI_PUBLIC int +ACL_EvalTestRights( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + char **rights, + char **map_generic, + char **deny_type, + char **deny_response, + char **acl_tag, + int *expr_num) +{ + ACLCachable_t cachable; + + return (ACL_INTEvalTestRights(errp, acleval, rights, map_generic, + deny_type, deny_response, + acl_tag, expr_num, &cachable)); +} + + +NSAPI_PUBLIC ACLEvalHandle_t * +ACL_EvalNew(NSErr_t *errp, pool_handle_t *pool) +{ + ACLEvalHandle_t *rv = ((ACLEvalHandle_t *)pool_calloc(pool, sizeof(ACLEvalHandle_t), 1)); + rv->default_result = ACL_RES_DENY; + return rv; +} + +NSAPI_PUBLIC void +ACL_EvalDestroy(NSErr_t *errp, pool_handle_t *pool, ACLEvalHandle_t *acleval) +{ + if (!acleval->acllist || acleval->acllist == ACL_LIST_NO_ACLS) + return; + NS_ASSERT(acleval->acllist->ref_count > 0); + + ACL_CritEnter(); + NS_ASSERT(ACL_CritHeld()); + if (--acleval->acllist->ref_count == 0) { + if (ACL_LIST_IS_STALE(acleval->acllist)) { + ACL_ListDestroy(errp, acleval->acllist); + } + } + ACL_CritExit(); + pool_free(pool, acleval); +} + +NSAPI_PUBLIC void +ACL_EvalDestroyNoDecrement(NSErr_t *errp, pool_handle_t *pool, ACLEvalHandle_t *acleval) +{ + /*if (!acleval->acllist || acleval->acllist == ACL_LIST_NO_ACLS) + return; */ + + /* olga: we need to free acleval unconditionally to avoid memory leaks */ + if (acleval) + pool_free(pool, acleval); +} + +NSAPI_PUBLIC int +ACL_ListDecrement(NSErr_t *errp, ACLListHandle_t *acllist) +{ + if (!acllist || acllist == ACL_LIST_NO_ACLS) + return 0; + + NS_ASSERT(ACL_AssertAcllist(acllist)); + + ACL_CritEnter(); + NS_ASSERT(ACL_CritHeld()); + if (--acllist->ref_count == 0) { + if (ACL_LIST_IS_STALE(acllist)) { + ACL_ListDestroy(errp, acllist); + } + } + ACL_CritExit(); + + return 0; +} + +NSAPI_PUBLIC int +ACL_EvalSetACL(NSErr_t *errp, ACLEvalHandle_t *acleval, ACLListHandle_t *acllist) +{ + NS_ASSERT(ACL_AssertAcllist(acllist)); + + acleval->acllist = acllist; + return(0); +} + +NSAPI_PUBLIC int +ACL_EvalSetSubject(NSErr_t *errp, ACLEvalHandle_t *acleval, PList_t subject) +{ + acleval->subject = subject; + return 0; +} + +NSAPI_PUBLIC PList_t +ACL_EvalGetSubject(NSErr_t *errp, ACLEvalHandle_t *acleval) +{ + return (acleval->subject); +} + +NSAPI_PUBLIC int +ACL_EvalSetResource(NSErr_t *errp, ACLEvalHandle_t *acleval, PList_t resource) +{ + acleval->resource = resource; + return 0; +} + +NSAPI_PUBLIC PList_t +ACL_EvalGetResource(NSErr_t *errp, ACLEvalHandle_t *acleval) +{ + return (acleval->resource); +} diff --git a/lib/libaccess/oneeval.h b/lib/libaccess/oneeval.h new file mode 100644 index 00000000..022570dd --- /dev/null +++ b/lib/libaccess/oneeval.h @@ -0,0 +1,17 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef LEVAL_H +#define LEVAL_H + +NSPR_BEGIN_EXTERN_C + +int +freeLAS(NSErr_t *errp, char *attribute, void **las_cookie); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/parse.h b/lib/libaccess/parse.h new file mode 100644 index 00000000..420bd913 --- /dev/null +++ b/lib/libaccess/parse.h @@ -0,0 +1,21 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * module private routines for handling the yacc based + * ACL Parser. + */ + +#ifndef PARSE_H +#define PARSE_H + +NSPR_BEGIN_EXTERN_C + +extern int acl_PushListHandle(ACLListHandle_t *handle); +extern int acl_Parse(void); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/permhash.h b/lib/libaccess/permhash.h new file mode 100644 index 00000000..f072be2b --- /dev/null +++ b/lib/libaccess/permhash.h @@ -0,0 +1,79 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _PERMHASH_H_ +#define _PERMHASH_H_ + +#include <string.h> +#include <plhash.h> +#include <base/pool.h> +#include <base/util.h> + +static void * +ACL_PermAllocTable(void *pool, PRSize size) +{ + return pool_malloc((pool_handle_t *)pool, size); +} + +static void +ACL_PermFreeTable(void *pool, void *item) +{ + pool_free((pool_handle_t *)pool, item); +} + +static PLHashEntry * +ACL_PermAllocEntry(void *pool, const void *unused) +{ + return ((PLHashEntry *)pool_malloc((pool_handle_t *)pool, sizeof(PLHashEntry))); +} + +static void +ACL_PermFreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + if (flag == HT_FREE_ENTRY) + pool_free((pool_handle_t *)pool, he); +} + +static PLHashAllocOps ACLPermAllocOps = { + ACL_PermAllocTable, + ACL_PermFreeTable, + ACL_PermAllocEntry, + ACL_PermFreeEntry +}; + +static int +PR_StringFree(PLHashEntry *he, int i, void *arg) +{ + PERM_FREE(he->key); + return 0; +} + +static PLHashNumber +PR_HashCaseString(const void *key) +{ + PLHashNumber h; + const unsigned char *s; + + h = 0; + for (s = (const unsigned char *)key; *s; s++) + h = (h >> 28) ^ (h << 4) ^ tolower(*s); + return h; +} + +static int +PR_CompareCaseStrings(const void *v1, const void *v2) +{ + const char *s1 = (const char *)v1; + const char *s2 = (const char *)v2; + +#ifdef XP_WIN32 + return (util_strcasecmp(s1, s2) == 0); +#else + return (strcasecmp(s1, s2) == 0); +#endif +} + + +#endif /* _PERMHASH_H */ diff --git a/lib/libaccess/register.cpp b/lib/libaccess/register.cpp new file mode 100644 index 00000000..2973e1d5 --- /dev/null +++ b/lib/libaccess/register.cpp @@ -0,0 +1,821 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * LAS registration interface + */ + +#include <netsite.h> +#include <plhash.h> +#include <base/systems.h> +#include <base/util.h> +#include <base/nsassert.h> +#include "permhash.h" +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/ldapacl.h> +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +/* This is to force aclspace.o into ns-httpd30.dll */ +static ACLGlobal_p *link_ACLGlobal = &ACLGlobal; + +/* This forces oneeval.o into ns-httpd30.dll */ +static ACLDispatchVector_t **link_nsacl_table = &__nsacl_table; + +ACLMethod_t ACLMethodDefault = ACL_METHOD_INVALID; +ACLDbType_t ACLDbTypeDefault = ACL_DBTYPE_INVALID; +static char *ACLDatabaseDefault = 0; + +ACLDbType_t ACL_DbTypeLdap = ACL_DBTYPE_INVALID; + +DbParseFn_t ACLDbParseFnTable[ACL_MAX_DBTYPE]; + +void +ACL_LasHashInit() +{ + int i; + + ACLLasEvalHash = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLLasEvalHash); + + ACLLasFlushHash = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLLasFlushHash); + + ACLMethodHash = PR_NewHashTable(ACL_MAX_METHOD, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLMethodHash); + + ACLDbTypeHash = PR_NewHashTable(ACL_MAX_DBTYPE, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLDbTypeHash); + + for (i = 0; i < ACL_MAX_DBTYPE; i++) + ACLDbParseFnTable[i] = 0; + + ACLAttrGetterHash = PR_NewHashTable(256, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLDbTypeHash); + + ACLDbNameHash = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + ACL_DATABASE_POOL); + NS_ASSERT(ACLDbNameHash); + + ACLUserLdbHash = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLUserLdbHash); + + return; +} + +void +ACL_LasHashDestroy() +{ + if (ACLLasEvalHash) { + PR_HashTableDestroy(ACLLasEvalHash); + ACLLasEvalHash=NULL; + } + if (ACLLasFlushHash) { + PR_HashTableDestroy(ACLLasFlushHash); + ACLLasFlushHash=NULL; + } +} + +/* ACL_LasRegister + * INPUT + * errp NSError structure + * attr_name E.g. "ip" or "dns" etc. + * eval_func E.g. LASIpEval + * flush_func Optional - E.g. LASIpFlush or NULL + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasRegister(NSErr_t *errp, char *attr_name, LASEvalFunc_t eval_func, +LASFlushFunc_t flush_func) +{ + + if ((!attr_name) || (!eval_func)) return -1; + + ACL_CritEnter(); + + /* See if the function is already registered. If so, report and + * error, but go ahead and replace it. + */ + if (PR_HashTableLookup(ACLLasEvalHash, attr_name) != NULL) { + nserrGenerate(errp, ACLERRDUPSYM, ACLERR3900, ACL_Program, 1, + attr_name); + } + + /* Put it in the hash tables */ + PR_HashTableAdd(ACLLasEvalHash, attr_name, (void *)eval_func); + PR_HashTableAdd(ACLLasFlushHash, attr_name, (void *)flush_func); + + ACL_CritExit(); + return 0; +} + +/* ACL_LasFindEval + * INPUT + * errp NSError pointer + * attr_name E.g. "ip" or "user" etc. + * eval_funcp Where the function pointer is returned. NULL if the + * function isn't registered. + * Must be called in a critical section as ACLEvalHash is a global + * variable. + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasFindEval(NSErr_t *errp, char *attr_name, LASEvalFunc_t *eval_funcp) +{ + + NS_ASSERT(attr_name); + if (!attr_name) return -1; + + *eval_funcp = (LASEvalFunc_t)PR_HashTableLookup(ACLLasEvalHash, attr_name); + return 0; +} + + +/* ACL_LasFindFlush + * INPUT + * errp NSError pointer + * attr_name E.g. "ip" or "user" etc. + * eval_funcp Where the function pointer is returned. NULL if the + * function isn't registered. + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasFindFlush(NSErr_t *errp, char *attr_name, LASFlushFunc_t *flush_funcp) +{ + + NS_ASSERT(attr_name); + if (!attr_name) return -1; + + *flush_funcp = (LASFlushFunc_t)PR_HashTableLookup(ACLLasFlushHash, attr_name); + return 0; +} + + +/* ACL_MethodRegister + * INPUT + * name Method name string. Can be freed after return. + * OUTPUT + * &t Place to return the Method_t (>0) + * retcode 0 on success, non-zero otherwise + */ + +int cur_method = 0; /* Use a static counter to generate the numbers */ + +NSAPI_PUBLIC int +ACL_MethodRegister(NSErr_t *errp, const char *name, ACLMethod_t *t) +{ + ACLMethod_t rv; + + ACL_CritEnter(); + + /* See if this is already registered */ + rv = (ACLMethod_t) PR_HashTableLookup(ACLMethodHash, name); + if (rv != NULL) { + *t = rv; + ACL_CritExit(); + return 0; + } + + /* To prevent the hash table from resizing, don't get to 32 entries */ + if (cur_method >= (ACL_MAX_METHOD-1)) { + ACL_CritExit(); + return -1; + } + + /* Put it in the hash table */ + rv = PR_HashTableAdd(ACLMethodHash, name, (void *)++cur_method); + *t = (ACLMethod_t) cur_method; + + ACL_CritExit(); + return 0; +} + +NSAPI_PUBLIC int +ACL_MethodFind(NSErr_t *errp, const char *name, ACLMethod_t *t) +{ + ACLMethod_t rv; + + /* Don't have to get the Critical Section lock 'cause the only danger + * would be if the hash table had to be resized. We created it with + * room for 32 entries before that happens. + */ + rv = (ACLMethod_t) PR_HashTableLookup(ACLMethodHash, name); + if (rv != NULL) { + *t = rv; + return 0; + } + + return -1; +} + +typedef struct HashEnumArg_s { + char **names; + int count; +} HashEnumArg_t; + +typedef HashEnumArg_t *HashEnumArg_p; + +static int acl_hash_enumerator (PLHashEntry *he, PRIntn i, void *arg) +{ + HashEnumArg_t *info = (HashEnumArg_t *)arg; + char **names = info->names; + + names[info->count++] = STRDUP((const char *)he->key); + + return names[info->count-1] ? 0 : -1; +} + +int acl_registered_names(PLHashTable *ht, int count, char ***names) +{ + HashEnumArg_t arg; + int rv; + + if (count == 0) { + *names = 0; + return 0; + } + + arg.names = (char **)MALLOC(count * sizeof(char *)); + arg.count = 0; + + if (!arg.names) return -1; + + rv = PR_HashTableEnumerateEntries(ht, acl_hash_enumerator, &arg); + + if (rv >= 0) { + /* success */ + *names = arg.names; + } + else { + *names = 0; + } + + return rv; +} + +NSAPI_PUBLIC int +ACL_MethodNamesGet(NSErr_t *errp, char ***names, int *count) +{ + *count = cur_method; + return acl_registered_names (ACLMethodHash, *count, names); +} + +NSAPI_PUBLIC int +ACL_MethodNamesFree(NSErr_t *errp, char **names, int count) +{ + int i; + + if (!names) return 0; + + for (i = count-1; i; i--) FREE(names[i]); + + FREE(names); + return 0; +} + +NSAPI_PUBLIC int +ACL_DbTypeFind(NSErr_t *errp, const char *name, ACLDbType_t *t) +{ + ACLDbType_t rv; + + /* Don't have to get the Critical Section lock 'cause the only danger + * would be if the hash table had to be resized. We created it with + * room for 32 entries before that happens. + */ + rv = (ACLDbType_t) PR_HashTableLookup(ACLDbTypeHash, name); + if (rv != NULL) { + *t = rv; + return 0; + } + + return -1; +} + +/* ACL_DbTypeRegister + * INPUT + * name DbType name string. Can be freed after return. + * OUTPUT + * &t Place to return the DbType (>0) + * retcode 0 on success, non-zero otherwise + */ + +int cur_dbtype = 0; /* Use a static counter to generate the numbers */ + +NSAPI_PUBLIC int +ACL_DbTypeRegister(NSErr_t *errp, const char *name, DbParseFn_t func, ACLDbType_t *t) +{ + ACLDbType_t rv; + + ACL_CritEnter(); + + /* See if this is already registered */ + rv = (ACLDbType_t) PR_HashTableLookup(ACLDbTypeHash, name); + if (rv != NULL) { + *t = rv; + ACLDbParseFnTable[(int)(PRSize)rv] = func; + ACL_CritExit(); + return 0; + } + + /* To prevent the hash table from resizing, don't get to 32 entries */ + if (cur_dbtype >= (ACL_MAX_DBTYPE-1)) { + ACL_CritExit(); + return -1; + } + + /* Put it in the hash table */ + rv = PR_HashTableAdd(ACLDbTypeHash, name, (void *)++cur_dbtype); + *t = (ACLDbType_t) cur_dbtype; + ACLDbParseFnTable[cur_dbtype] = func; + + ACL_CritExit(); + return 0; +} + + +NSAPI_PUBLIC int +ACL_DbTypeIsRegistered (NSErr_t *errp, const ACLDbType_t t) +{ + return (0 < ((int)(PRSize)t) && ((int)(PRSize)t) <= cur_dbtype); +} + + +/* ACL_MethodIsEqual + * RETURNS non-zero if equal. + */ +NSAPI_PUBLIC int +ACL_MethodIsEqual(NSErr_t *errp, const ACLMethod_t t1, const ACLMethod_t t2) +{ + return (t1 == t2); +} + + +/* ACL_DbTypeIsEqual + * RETURNS non-zero if equal. + */ +NSAPI_PUBLIC int +ACL_DbTypeIsEqual(NSErr_t *errp, const ACLDbType_t t1, const ACLDbType_t t2) +{ + return (t1 == t2); +} + + +/* ACL_MethodNameIsEqual + * Takes a method type and a method name and sees if they match. + * Returns non-zero on match. + */ +NSAPI_PUBLIC int +ACL_MethodNameIsEqual(NSErr_t *errp, const ACLMethod_t t1, const char *name) +{ + int rv; + ACLMethod_t t2; + + rv = ACL_MethodFind(errp, name, &t2); + if (rv) + return (rv); + else + return (t1 == t2); +} + +/* ACL_DbTypeNameIsEqual + * Takes a dbtype type and a dbtype name and sees if they match. + * Returns non-zero on match. + */ +NSAPI_PUBLIC int +ACL_DbTypeNameIsEqual(NSErr_t *errp, const ACLDbType_t t1, const char *name) +{ + int rv; + ACLDbType_t t2; + + rv = ACL_DbTypeFind(errp, name, &t2); + if (rv) + return (rv); + else + return (t1 == t2); +} + +/* ACL_MethodGetDefault + */ +NSAPI_PUBLIC ACLMethod_t +ACL_MethodGetDefault(NSErr_t *errp) +{ + return (ACLMethodDefault); +} + +/* ACL_MethodSetDefault + */ +NSAPI_PUBLIC int +ACL_MethodSetDefault(NSErr_t *errp, const ACLMethod_t t) +{ + ACLMethodDefault = t; + return 0; +} + + +/* ACL_DbTypeGetDefault + */ +NSAPI_PUBLIC ACLDbType_t +ACL_DbTypeGetDefault(NSErr_t *errp) +{ + return (ACLDbTypeDefault); +} + +/* ACL_DbTypeSetDefault + */ +NSAPI_PUBLIC int +ACL_DbTypeSetDefault(NSErr_t *errp, ACLDbType_t t) +{ + ACLDbTypeDefault = t; + return 0; +} + + +/* ACL_DatabaseGetDefault + */ +NSAPI_PUBLIC const char * +ACL_DatabaseGetDefault(NSErr_t *errp) +{ + return (ACLDatabaseDefault); +} + +/* ACL_DatabaseSetDefault + */ +NSAPI_PUBLIC int +ACL_DatabaseSetDefault(NSErr_t *errp, const char *dbname) +{ + ACLDbType_t dbtype; + int rv; + void *db; + + if (!dbname || !*dbname) return LAS_EVAL_FAIL; + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, &db); + + if (rv != LAS_EVAL_TRUE) return -1; + + if (ACLDatabaseDefault) pool_free(ACL_DATABASE_POOL, ACLDatabaseDefault); + + ACL_DbTypeSetDefault(errp, dbtype); + ACLDatabaseDefault = pool_strdup(ACL_DATABASE_POOL, dbname); + + return ACLDatabaseDefault ? 0 : -1; +} + + +/* ACL_AuthInfoGetMethod + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * *t The Method number. This can be the default method + number if the auth_info PList doesn't explicitly have a Method entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetMethod(NSErr_t *errp, PList_t auth_info, ACLMethod_t *t) +{ + ACLMethod_t *methodp; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_METHOD_INDEX, (void **)&methodp, NULL) < 0) + { + /* No entry for "method" */ + *t = ACLMethodDefault; + } else { + *t = *methodp; + } + + return 0; +} + + +/* ACL_AuthInfoSetMethod + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * t The Method number. + * OUTPUT + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoSetMethod(NSErr_t *errp, PList_t auth_info, ACLMethod_t t) +{ + ACLMethod_t *methodp; + int rv; + + if (auth_info) { + rv = PListGetValue(auth_info, ACL_ATTR_METHOD_INDEX, (void **)&methodp, + NULL); + + if (rv < 0) { + /* No entry for "method" */ + methodp = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); + if (!methodp) return -1; + *methodp = t; + PListInitProp(auth_info, ACL_ATTR_METHOD_INDEX, ACL_ATTR_METHOD, methodp, 0); + } + else { + /* replace the old entry */ + if (!methodp) return -1; + *methodp = t; + } + } + else { + return -1; + } + + return 0; +} + + +/* ACL_AuthInfoSetDbname + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * dbname Name of the new auth_info database. + * OUTPUT + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoSetDbname(NSErr_t *errp, PList_t auth_info, const char *dbname) +{ + ACLDbType_t *dbtype = (ACLDbType_t *)PERM_MALLOC(sizeof(ACLDbType_t)); + ACLDbType_t *t2; + char *copy; + char *n2; + void *db; + int old1; + int old2; + int rv; + + if (!dbtype) { + /* out of memory */ + return -1; + } + + if (auth_info) { + rv = ACL_DatabaseFind(errp, dbname, dbtype, (void **)&db); + + if (rv != LAS_EVAL_TRUE) { + PERM_FREE(dbtype); + return -1; + } + + /* Check the existing entry */ + old1 = PListGetValue(auth_info, ACL_ATTR_DBTYPE_INDEX, (void **)&t2, + NULL); + old2 = PListGetValue(auth_info, ACL_ATTR_DATABASE_INDEX, (void **)&n2, + NULL); + + if (old1 >= 0 && old2 >= 0) { + /* check if the old entry is same */ + if (ACL_DbTypeIsEqual(errp, *dbtype, *t2)) { + /* Nothing to do */ + PERM_FREE(dbtype); + return 0; + } + } + /* free the old entries */ + if (old1 >= 0) { + PListDeleteProp(auth_info, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE); + PERM_FREE(t2); + } + if (old2 >= 0) { + PListDeleteProp(auth_info, ACL_ATTR_DATABASE_INDEX, ACL_ATTR_DATABASE); + PERM_FREE(n2); + } + + /* Create new entries for "dbtype" & "dbname" */ + copy = (char *)PERM_STRDUP(dbname); + if (!copy) return -1; + PListInitProp(auth_info, ACL_ATTR_DATABASE_INDEX, + ACL_ATTR_DATABASE, copy, 0); + PListInitProp(auth_info, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE, + dbtype, 0); + } + else { + return -1; + } + + return 0; +} + + +/* ACL_AuthInfoGetDbType + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * *t The DbType number. This can be the default dbtype + * number if the auth_info PList doesn't explicitly + * have a DbType entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetDbType(NSErr_t *errp, PList_t auth_info, ACLDbType_t *t) +{ + ACLDbType_t *dbtypep; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_DBTYPE_INDEX, (void **)&dbtypep, NULL) < 0) + { + /* No entry for "dbtype" */ + *t = ACLDbTypeDefault; + } else { + *t = *dbtypep; + } + + return 0; +} + +/* ACL_AuthInfoGetDbname + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * dbname The database name. This can be the default database + * name if the auth_info PList doesn't explicitly + * have a database entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetDbname(PList_t auth_info, char **dbname) +{ + char *dbstr; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_DATABASE_INDEX, (void **)&dbstr, NULL) < 0) + { + /* No entry for "database" */ + dbstr = ACLDatabaseDefault; + } + + /* else the value was already set by the PListGetValue call */ + *dbname = dbstr; + return 0; +} + +NSAPI_PUBLIC DbParseFn_t +ACL_DbTypeParseFn(NSErr_t *errp, const ACLDbType_t dbtype) +{ + if (ACL_DbTypeIsRegistered(errp, dbtype)) + return ACLDbParseFnTable[(int)(PRSize)dbtype]; + else + return 0; +} + +/* The hash table is keyed by attribute name, and contains pointers to the + * PRCList headers. These in turn, circularly link a set of AttrGetter_s + * structures. + */ +NSAPI_PUBLIC int +ACL_AttrGetterRegister(NSErr_t *errp, const char *attr, ACLAttrGetterFn_t fn, + ACLMethod_t m, ACLDbType_t d, int position, void *arg) +{ + ACLAttrGetter_t *getter; + PLHashEntry **hep; + + if (position != ACL_AT_FRONT && position != ACL_AT_END) { + return -1; + } + + ACL_CritEnter(); + + hep = PR_HashTableRawLookup(ACLAttrGetterHash, PR_HashCaseString(attr), attr); + + /* Now, allocate the current entry */ + getter = (ACLAttrGetter_t *)CALLOC(sizeof(ACLAttrGetter_t)); + if (getter == NULL) { + ACL_CritExit(); + return -1; + } + getter->method = m; + getter->dbtype = d; + getter->fn = fn; + getter->arg = arg; + + if (*hep == 0) { /* New entry */ + + PR_INIT_CLIST(&getter->list); + PR_HashTableAdd(ACLAttrGetterHash, attr, (void *)getter); + } + else { + + ACLAttrGetter_t *head = (ACLAttrGetter_t *)((*hep)->value); + + PR_INSERT_BEFORE(&getter->list, &head->list); + + if (position == ACL_AT_FRONT) { + + /* Set new head of list */ + (*hep)->value = (void *)getter; + } + } + + ACL_CritExit(); + return 0; +} + +NSAPI_PUBLIC int +ACL_AttrGetterFind(NSErr_t *errp, const char *attr, + ACLAttrGetterList_t *getters) +{ + *getters = PR_HashTableLookup(ACLAttrGetterHash, attr); + if (*getters) + return 0; + else + return -1; +} + +NSAPI_PUBLIC +ACLAttrGetter_t * ACL_AttrGetterFirst(ACLAttrGetterList_t *getters) +{ + ACLAttrGetter_t * first = 0; + + if (getters && *getters) { + + first = (ACLAttrGetter_t *)(*getters); + } + + return first; +} + +NSAPI_PUBLIC ACLAttrGetter_t * +ACL_AttrGetterNext(ACLAttrGetterList_t *getters, ACLAttrGetter_t *last) +{ + ACLAttrGetter_t *head; + ACLAttrGetter_t *next = 0; + + if (getters && *getters && last) { + + head = (ACLAttrGetter_t *)(*getters); + if (head) { + + /* End of list? */ + if (last != (ACLAttrGetter_t *)PR_LIST_TAIL(&head->list)) { + + /* No, get next entry */ + next = (ACLAttrGetter_t *)PR_NEXT_LINK(&last->list); + } + } + } + + return next; +} + +int +ACL_RegisterInit () +{ + NSErr_t *errp = 0; + int rv; + + /* Register the ldap database */ + rv = ACL_DbTypeRegister(errp, ACL_DBTYPE_LDAP, parse_ldap_url, &ACL_DbTypeLdap); + + return rv; +} + diff --git a/lib/libaccess/register.h b/lib/libaccess/register.h new file mode 100644 index 00000000..981e2cb8 --- /dev/null +++ b/lib/libaccess/register.h @@ -0,0 +1,98 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef ACL_REGISTER_HEADER +#define ACL_REGISTER_HEADER + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include <libaccess/las.h> + +typedef void * ACLMethod_t; +#define ACL_METHOD_ANY (ACLMethod_t)-1 +#define ACL_METHOD_INVALID (ACLMethod_t)-2 +typedef void * ACLDbType_t; +#define ACL_DBTYPE_ANY (ACLDbType_t)-1 +#define ACL_DBTYPE_INVALID (ACLDbType_t)-2 + +typedef struct ACLGetter_s { + ACLMethod_t method; + ACLDbType_t db; + AttrGetterFn fn; +} ACLGetter_t; +typedef ACLGetter_s * ACLGetter_p; + +/* + * Command values for the "position" argument to ACL_RegisterGetter + * Any positive >0 value is the specific position in the list to insert + * the new function. + */ +#define ACL_AT_FRONT 0 +#define ACL_AT_END -1 +#define ACL_REPLACE_ALL -2 +#define ACL_REPLACE_MATCHING -3 + +#ifdef ACL_LIB_INTERNAL +#define ACL_MAX_METHOD 32 +#define ACL_MAX_DBTYPE 32 +#endif + +NSPR_BEGIN_EXTERN_C + +NSAPI_PUBLIC extern int + ACL_LasRegister( NSErr_t *errp, char *attr_name, LASEvalFunc_t + eval_func, LASFlushFunc_t flush_func ); +NSAPI_PUBLIC extern int + ACL_LasFindEval( NSErr_t *errp, char *attr_name, LASEvalFunc_t + *eval_funcp ); +NSAPI_PUBLIC extern int + ACL_LasFindFlush( NSErr_t *errp, char *attr_name, LASFlushFunc_t + *flush_funcp ); +extern void + ACL_LasHashInit( void ); +extern void + ACL_LasHashDestroy( void ); + +/* + * Revised, normalized method/dbtype registration routines + */ +NSAPI_PUBLIC extern int + ACL_MethodRegister(const char *name, ACLMethod_t *t); +NSAPI_PUBLIC extern int + ACL_MethodIsEqual(ACLMethod_t t1, ACLMethod_t t2); +NSAPI_PUBLIC extern int + ACL_MethodNameIsEqual(ACLMethod_t t, const char *name); +NSAPI_PUBLIC extern int + ACL_MethodFind(const char *name, ACLMethod_t *t); +NSAPI_PUBLIC extern ACLMethod_t + ACL_MethodGetDefault(); +NSAPI_PUBLIC extern void + ACL_MethodSetDefault(); +NSAPI_PUBLIC extern int + ACL_AuthInfoGetMethod(PList_t auth_info, ACLMethod_t *t); + +NSAPI_PUBLIC extern int + ACL_DbTypeRegister(const char *name, DbParseFn_t func, ACLDbType_t *t); +NSAPI_PUBLIC extern int + ACL_DbTypeIsEqual(ACLDbType_t t1, ACLDbType_t t2); +NSAPI_PUBLIC extern int + ACL_DbTypeNameIsEqual(ACLDbType_t t, const char *name); +NSAPI_PUBLIC extern int + ACL_DbTypeFind(const char *name, ACLDbType_t *t); +NSAPI_PUBLIC extern ACLDbType_t + ACL_DbTypeGetDefault(); +NSAPI_PUBLIC extern void + ACL_DbTypeSetDefault(); +NSAPI_PUBLIC extern int + ACL_AuthInfoGetDbType(PList_t auth_info, ACLDbType_t *t); + +NSAPI_PUBLIC extern int + ACL_RegisterGetter(AttrGetterFn fn, ACLMethod_t m, ACLDbType_t d, int + position, void *arg); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/symbols.cpp b/lib/libaccess/symbols.cpp new file mode 100644 index 00000000..ca1e18b1 --- /dev/null +++ b/lib/libaccess/symbols.cpp @@ -0,0 +1,350 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (symbols.c) + * + * This module implements a symbol table for ACL-related structures. + * The symbol table associates string names and types with pointers + * to various kinds of structures. + */ +/* +#include <base/systems.h> +*/ +#include <plhash.h> +#include <base/util.h> +#include <netsite.h> +#define __PRIVATE_SYMBOLS +#include "libaccess/symbols.h" +#include <ctype.h> + +static PLHashEntry * symAllocEntry(void * pool, const void *unused); +static void * symAllocTable(void * pool, PRSize size); +static int symCmpName(const void * name1, const void * name2); +static int symCmpValue(const void * value1, const void * value2); +static PLHashNumber symHash(const void * symkey); +static void symFreeEntry(void * pool, PLHashEntry * he, PRUintn flag); +static void symFreeTable(void * pool, void * item); + +/* Table of pointers to functions associated with the hash table */ +static PLHashAllocOps SymAllocOps = { + symAllocTable, /* allocate the hash table */ + symFreeTable, /* free the hash table */ + symAllocEntry, /* allocate a table entry */ + symFreeEntry, /* free a table entry */ +}; + +static void * symAllocTable(void * pool, PRSize size) +{ + return (void *)PERM_MALLOC(size); +} + +static void symFreeTable(void * pool, void * item) +{ + PERM_FREE(item); +} + + + +static PLHashEntry * symAllocEntry(void * pool, const void *ignored) +{ + PLHashEntry * he; + + he = (PLHashEntry *) PERM_MALLOC(sizeof(PLHashEntry)); + + return he; +} + +static void symFreeEntry(void * pool, PLHashEntry * he, PRUintn flag) +{ + if (flag == HT_FREE_ENTRY) { + /* Just free the hash entry, not anything it references */ + PERM_FREE(he); + } +} + + +static int symCmpName(const void * name1, const void * name2) +{ + Symbol_t * sym1 = (Symbol_t *)name1; + Symbol_t * sym2 = (Symbol_t *)name2; + + return ((sym1->sym_type == sym2->sym_type) && + !strcasecmp(sym1->sym_name, sym2->sym_name)); +} + +static int symCmpValue(const void * value1, const void * value2) +{ + return (value1 == value2); +} + +static PLHashNumber symHash(const void * symkey) +{ + Symbol_t * sym = (Symbol_t *)symkey; + char * cp; + PLHashNumber h; + + h = sym->sym_type; + cp = sym->sym_name; + if (cp) { + while (*cp) { + h = (h << 3) ^ tolower(*cp); + ++cp; + } + } + + return h; +} + +/* Helper function for symTableEnumerate() */ +typedef struct { + int (*func)(Symbol_t * sym, void * parg); + void * argp; +} SymTableEnum_t; + +static int symTableEnumHelp(PLHashEntry * he, int n, void * step) +{ + SymTableEnum_t * ste = (SymTableEnum_t *)step; + int ret = 0; + int rv; + + rv = (*ste->func)((Symbol_t *)(he->key), ste->argp); + if (rv != 0) { + if (rv & SYMENUMREMOVE) ret = HT_ENUMERATE_REMOVE; + if (rv & SYMENUMSTOP) ret |= HT_ENUMERATE_STOP; + } + + return ret; +} + +NSPR_BEGIN_EXTERN_C + +/* + * Description (symTableAddSym) + * + * This function adds a symbol definition to the symbol table. + * The symbol definition includes a name string, a type, and a + * reference to a structure. + * + * Arguments: + * + * table - handle for symbol table + * newsym - pointer to new symbol name and type + * symref - pointer to structure named by symbol + * + * Returns: + * + * If successful, the return code is zero. An error is indicated + * by a negative return code (SYMERRxxxx - see symbols.h). + */ + +int symTableAddSym(void * table, Symbol_t * newsym, void * symref) +{ + SymTable_t * st = (SymTable_t *)table; + PLHashEntry * he; + PLHashEntry **hep; + PLHashNumber keyhash; + int rv = 0; + + /* Compute the hash value for this symbol */ + keyhash = symHash((const void *)newsym); + + crit_enter(st->stb_crit); + + /* See if another symbol already has the same name and type */ + hep = PL_HashTableRawLookup(st->stb_ht, keyhash, (void *)newsym); + if (*hep == 0) { + + /* Expand the hash table if necessary and allocate an entry */ + he = PL_HashTableRawAdd(st->stb_ht, + hep, keyhash, (void *)newsym, symref); + } + else { + /* The symbol is already there. It's an error */ + rv = SYMERRDUPSYM; + } + + crit_exit(st->stb_crit); + return rv; +} + +/* + * Description (symTableRemoveSym) + * + * This function removes an entry from a symbol table. It does + * not free the entry itself, just the hash entry that references + * it. + * + * Arguments: + * + * table - symbol table handle + * sym - pointer to symbol structure + */ + +void symTableRemoveSym(void * table, Symbol_t * sym) +{ + SymTable_t * st = (SymTable_t *)table; + + if (sym->sym_name != 0) { + crit_enter(st->stb_crit); + PL_HashTableRemove(st->stb_ht, (void *)sym); + crit_exit(st->stb_crit); + } +} + +/* + * Description (symTableEnumerate) + * + * This function enumerates all of the entries in a symbol table, + * calling a specified function for each entry. The function + * specified by the caller may return flags indicating actions + * to be taken for each entry or whether to terminate the + * enumeration. These flags are defined in symbols.h as + * SYMENUMxxxx. + * + * Arguments: + * + * table - symbol table handle + * argp - argument for caller function + * func - function to be called for each entry + */ + +void symTableEnumerate(void * table, void * argp, +#ifdef UnixWare /* Fix bug in UnixWare compiler for name mangeling - nedh@sco.com */ + ArgFn_symTableEnum func) +#else + int (*func)(Symbol_t * sym, void * parg)) +#endif +{ + SymTable_t * st = (SymTable_t *)table; + SymTableEnum_t ste; /* enumeration arguments */ + + ste.func = func; + ste.argp = argp; + + crit_enter(st->stb_crit); + (void)PL_HashTableEnumerateEntries(st->stb_ht, + symTableEnumHelp, (void *)&ste); + crit_exit(st->stb_crit); +} + +/* + * Description (symTableFindSym) + * + * This function locates a symbol with a specified name and type + * in a given symbol table. It returns a pointer to the structure + * named by the symbol. + * + * Arguments: + * + * table - symbol table handle + * symname - symbol name string pointer + * symtype - symbol type code + * psymref - pointer to returned structure pointer + * + * Returns: + * + * If successful, the return code is zero and the structure pointer + * associated with the symbol name and type is returned in the + * location specified by 'psymref'. An error is indicated by a + * negative return code (SYMERRxxxx - see symbols.h). + */ + +int symTableFindSym(void * table, char * symname, + int symtype, void **psymref) +{ + SymTable_t * st = (SymTable_t *)table; + Symbol_t sym; + void * symref; + + /* Create temporary entry with fields needed by symHash() */ + sym.sym_name = symname; + sym.sym_type = symtype; + + crit_enter(st->stb_crit); + + symref = PL_HashTableLookup(st->stb_ht, (void *)&sym); + + crit_exit(st->stb_crit); + + *psymref = symref; + + return (symref) ? 0 : SYMERRNOSYM; +} + +/* + * Description (symTableDestroy) + * + * This function destroys a symbol table created by symTableNew(). + * + * Arguments: + * + * table - symbol table handle from symTableNew() + * flags - bit flags (unused - must be zero) + */ + +void symTableDestroy(void * table, int flags) +{ + SymTable_t * st = (SymTable_t *)table; + + if (st) { + if (st->stb_crit) { + crit_terminate(st->stb_crit); + } + + if (st->stb_ht) { + PL_HashTableDestroy(st->stb_ht); + } + + PERM_FREE(st); + } +} + +/* + * Description (symTableNew) + * + * This function creates a new symbol table, and returns a handle + * for it. + * + * Arguments: + * + * ptable - pointer to returned symbol table handle + * + * Returns: + * + * If successful, the return code is zero and a handle for the new + * symbol table is returned in the location specified by 'ptable'. + * An error is indicated by a negative return code (SYMERRxxxx + * - see symbols.h). + */ + +int symTableNew(void **ptable) +{ + SymTable_t * st; + + /* Allocate the symbol table object */ + st = (SymTable_t *)PERM_MALLOC(sizeof(SymTable_t)); + if (st == 0) goto err_nomem; + + /* Get a monitor for it */ + st->stb_crit = crit_init(); + + st->stb_ht = PL_NewHashTable(0, symHash, symCmpName, symCmpValue, + &SymAllocOps, 0); + if (st->stb_ht == 0) goto err_nomem; + + *ptable = st; + return 0; + + err_nomem: + if (st) { + symTableDestroy(st, 0); + } + return SYMERRNOMEM; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/userauth.cpp b/lib/libaccess/userauth.cpp new file mode 100644 index 00000000..3413fbd0 --- /dev/null +++ b/lib/libaccess/userauth.cpp @@ -0,0 +1,12 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* userauth.c + * This file contain code to authenticate user. + */ + + + diff --git a/lib/libaccess/usi.cpp b/lib/libaccess/usi.cpp new file mode 100644 index 00000000..677f3bd7 --- /dev/null +++ b/lib/libaccess/usi.cpp @@ -0,0 +1,371 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/usi.h" + +/* + * Description (usiAlloc) + * + * This function is used to initialize a USIList_t structure to + * reference an array of unsigned integers, where the size of the + * array is specified. The caller is responsible for initializing + * the specified number of values in the array. + * + * Arguments: + * + * uilptr - pointer to list head + * count - number of entries to allocate + * + * Returns: + * + * If successful, a pointer to the USI_t array now referenced by the + * list head (uilptr) is returned. An error is indicated by a null + * return value. + */ + +USI_t * usiAlloc(USIList_t * uilptr, int count) +{ + /* Is there space already allocated for this list? */ + if (uilptr->uil_size > 0) { + + /* If it's not big enough to hold the desired size, free it */ + if (count > uilptr->uil_size) { + FREE(uilptr->uil_list); + UILINIT(uilptr); + } + } + + /* Do we need space? */ + if (uilptr->uil_size < count) { + + /* Yes, allocate space for the specified number of values */ + uilptr->uil_list = (USI_t *)MALLOC(sizeof(USI_t) * count); + if (uilptr->uil_list == 0) { + + /* Error - no memory available */ + uilptr->uil_count = 0; + return 0; + } + + uilptr->uil_size = count; + } + + uilptr->uil_count = count; + + return uilptr->uil_list; +} + +/* + * Description (usiInsert) + * + * This function is called to insert a specified USI_t value into + * a given list of USI_t values. The values are maintained in an + * array, where they are kept in ascending order. Duplicate values + * are rejected. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to be inserted + * + * Returns: + * + * If the specified value is already in the list, zero is returned. + * If the value is successfully inserted into the list, +1 is + * returned. An error is indicated by a negative return value. + */ + +int usiInsert(USIList_t * uilptr, USI_t usi) +{ + int ilow, ihigh, i; + USI_t * ids; + + ids = uilptr->uil_list; + + /* Binary search for specified group id */ + i = 0; + for (ilow = 0, ihigh = uilptr->uil_count; ilow != ihigh; ) { + + i = (ilow + ihigh) >> 1; + if (usi == ids[i]) { + /* The value is already in the list */ + return 0; + } + + if (usi > ids[i]) { + ilow = i + 1; + } + else { + ihigh = i; + } + } + + /* Check for empty list */ + if (uilptr->uil_count <= 0) { + + /* Any space allocated for the list yet? */ + if (uilptr->uil_size <= 0) { + + /* No, allocate some initial space */ + ids = (USI_t *) MALLOC(sizeof(USI_t) * 4); + if (ids == 0) { + /* Error - no memory available */ + return -1; + } + + uilptr->uil_size = 4; + uilptr->uil_list = ids; + } + + /* Value will be inserted at ilow, which is zero */ + } + else { + + /* + * Set ilow to the index at which the specified value + * should be inserted. + */ + if (usi > ids[i]) ++i; + ilow = i; + + /* Is there space for another value? */ + if (uilptr->uil_count >= uilptr->uil_size) { + + /* No, expand the array to hold more values */ + ids = (USI_t *)REALLOC(ids, + (uilptr->uil_size + 4) * sizeof(USI_t)); + if (ids == 0) { + /* Error - no memory available */ + return -1; + } + + uilptr->uil_size += 4; + uilptr->uil_list = ids; + } + + /* Copy higher values up */ + for (i = uilptr->uil_count; i > ilow; --i) { + ids[i] = ids[i-1]; + } + } + + /* Add the new value */ + ids[ilow] = usi; + uilptr->uil_count += 1; + + return 1; +} + +/* + * Description (usiPresent) + * + * This function is called to check whether a specified USI_t value + * is present in a given list. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to check for + * + * Returns: + * + * The return value is the index of the value in the list, plus one, + * if the value is present in the list, 0 if it is not. + */ + +int usiPresent(USIList_t * uilptr, USI_t usi) +{ + int ilow, ihigh, i; + USI_t * ids; + + ids = uilptr->uil_list; + + /* Binary search for specified group id */ + i = 0; + for (ilow = 0, ihigh = uilptr->uil_count; ilow != ihigh; ) { + + i = (ilow + ihigh) >> 1; + if (usi == ids[i]) { + /* The value is in the list */ + return i + 1; + } + + if (usi > ids[i]) { + ilow = i + 1; + } + else { + ihigh = i; + } + } + + /* The value was not found */ + return 0; +} + +/* + * Description (usiRemove) + * + * This function is called to remove a specified USI_t value from + * a given list. The list is compressed when the value is removed. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to be removed + * + * Returns: + * + * Returns the value returned by usiPresent(uilptr, usi). + */ + +int usiRemove(USIList_t * uilptr, USI_t usi) +{ + USI_t * ids; + int i, j; + + i = usiPresent(uilptr, usi); + if (i > 0) { + + /* Compress the remaining values */ + ids = uilptr->uil_list; + for (j = i ; j < uilptr->uil_count; ++j) { + ids[j-1] = ids[j]; + } + + /* Decrement the number of values and free space if none left */ + if (--uilptr->uil_count <= 0) { + FREE(uilptr->uil_list); + UILINIT(uilptr); + } + } + + return i; +} + +/* + * Description (uilDuplicate) + * + * This function is called to make a duplicate of a specified + * source list, in a given destination list. Any existing list + * referenced by the destination list head is either overwritten + * or replaced with a newly allocated list. The values in the + * source list are copied to the destination. Note that the + * destination list area may be larger than the source list area + * on return, i.e. their uil_size values may differ. + * + * Arguments: + * + * dstptr - pointer to destination list head + * srcptr - pointer to source list head + * + * Returns: + * + * The number of elements in the source and destination lists is + * returned if successful. An error is indicated by a negative + * return value. + */ + +int uilDuplicate(USIList_t * dstptr, USIList_t * srcptr) +{ + USI_t * idlist; + USI_t * srclist; + int count; + int i; + + count = srcptr->uil_count; + srclist = srcptr->uil_list; + + /* Allocate enough space in the destination list */ + idlist = usiAlloc(dstptr, count); + if ((idlist == 0) && (count > 0)) { + /* Error - insufficient memory */ + return -1; + } + + /* Copy source values to destination */ + for (i = 0; i < count; ++i) { + idlist[i] = srclist[i]; + } + + /* Return number of entries in destination list */ + return count; +} + +/* + * Description (uilMerge) + * + * This function is called to merge the values in a source list + * into a destination list. That is, any values in the source + * list which are not in the destination list will be inserted + * in it. + * + * Arguments: + * + * dstptr - pointer to destination list head + * srcptr - pointer to source list head + * + * Returns: + * + * The resulting number of elements in the destination list is + * returned if successful. An error is indicated by a negative + * return value. + */ + +int uilMerge(USIList_t * dstptr, USIList_t * srcptr) +{ + USIList_t mglist; /* list head for merged list */ + USI_t * srclist = srcptr->uil_list; + USI_t * dstlist = dstptr->uil_list; + int isrc, idst; + int scnt, dcnt; + int rv; + + UILINIT(&mglist); + + scnt = srcptr->uil_count; + dcnt = dstptr->uil_count; + isrc = 0; + idst = 0; + + while ((isrc < scnt) && (idst < dcnt)) { + + if (srclist[isrc] >= dstlist[idst]) { + rv = usiInsert(&mglist, dstlist[idst]); + if (rv < 0) goto punt; + if (srclist[isrc] == dstlist[idst]) ++isrc; + ++idst; + } + else if (srclist[isrc] < dstlist[idst]) { + rv = usiInsert(&mglist, srclist[isrc]); + if (rv < 0) goto punt; + ++isrc; + } + } + + while (isrc < scnt) { + rv = usiInsert(&mglist, srclist[isrc]); + if (rv < 0) goto punt; + ++isrc; + } + + while (idst < dcnt) { + rv = usiInsert(&mglist, dstlist[idst]); + if (rv < 0) goto punt; + ++idst; + } + + UILREPLACE(dstptr, &mglist); + + return dstptr->uil_count; + + punt: + UILFREE(&mglist); + return rv; +} + 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; +} + diff --git a/lib/libaccess/utest.mk b/lib/libaccess/utest.mk new file mode 100644 index 00000000..f33e6e6d --- /dev/null +++ b/lib/libaccess/utest.mk @@ -0,0 +1,61 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +#CFLAGS = -g -DDEBUG -I. +CFLAGS = -g -I. -I../../../include $(TESTFLAGS) +#LEX = flex +CC=gcc + +HEAD = aclparse.h acltools.h lparse.h acl.h acleval.h lasdns.h lasip.h mthash.h stubs.h aclscan.h acl.tab.h +XSRC = aclparse.y aclscan.l +CSRC = acleval.c aclutil.c lasdns.c lasip.c lastod.c mthash.c testmain.c acltools.c space.c acl.tab.c acl.yy.c +SRC = $(HEAD) $(XSRC) $(CSRC) + +XOBJ = acl.tab.o acl.yy.o testmain.o acltools.o +COBJ = $(CSRC:%.c=%.o) +OBJ = $(XOBJ) $(COBJ) + +always: $(OBJ) + +acleval.o: stubs.h aclparse.h acl.h acleval.h mthash.h + +aclutil.o: acl.h aclparse.h + +lasdns.o: acl.h aclparse.h lasdns.h mthash.h + +lasip.o: acl.h aclparse.h lasip.h + +lastod.o: acl.h aclparse.h + +acltools.o: aclparse.h aclscan.h lparse.h aclparse.y + +testmain.o: aclparse.h acltools.h + +acl.yy.o: acl.yy.c acl.tab.h + +acl.yy.o acl.tab.o acltools.o: aclparse.h acltools.h lparse.h + +yacc: aclparse.y + $(YACC) -dv aclparse.y + mv y.tab.h acl.tab.h + mv y.tab.c acl.tab.c +#sed -f yy-sed y.tab.h > acl.tab.h +#sed -f yy-sed y.tab.c > acl.tab.c + +# Should only run this on an SGI, where flex() is present +flex: aclscan.l + $(LEX) aclscan.l + mv lex.yy.c acl.yy.c +#sed -f yy-sed lex.yy.c > acl.yy.c + +clean: + rm -f aclparse aclparse.pure y.output acl.tab.c acl.tab.h acl.yy.c lex.yy.c y.tab.c y.tab.h aclparse.c $(OBJ) + +# Check it out from the RCS directory +$(SRC): RCS/$$@,v + co $@ diff --git a/lib/libaccess/utest/.purify b/lib/libaccess/utest/.purify new file mode 100644 index 00000000..56b9983e --- /dev/null +++ b/lib/libaccess/utest/.purify @@ -0,0 +1,19 @@ +suppress umr process_gethost +suppress umr _door_gethostbyname_r +suppress umr _get_hostserv_inetnetdir_byname +suppress umr _get_hostserv_inetnetdir_byaddr +suppress umr gethostbyname_r +suppress umr _nsc_trydoorcall +suppress umr LASDnsBuild +suppress umr PR_HashString +suppress umr mthsearch +suppress umr Hash +suppress umr strcmp +suppress umr mthsearch +suppress umr strlen +suppress umr strdup +suppress umr strcpy +suppress umr PListFindValue +suppress umr LASIpEval +suppress umr LASDnsEval +suppress mlk system_strdup_perm diff --git a/lib/libaccess/utest/Makefile b/lib/libaccess/utest/Makefile new file mode 100644 index 00000000..2acedff0 --- /dev/null +++ b/lib/libaccess/utest/Makefile @@ -0,0 +1,119 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# +MCOM_ROOT=../../../.. +MODULE=LibAcl +include ../../../nsdefs.mk + +OBJDEST=. +UTESTDEST=utest + +include ../../../nsconfig.mk + +MODULE_CFLAGS=-I$(NSROOT)/include/libaccess -I$(NSROOT)/include -I$(NSROOT)/include/public -I. -DACL_LIB_INTERNAL + +include $(INCLUDE_DEPENDS) + +#TESTFLAGS = -DUTEST -DDEBUG_LEVEL_2 +TESTFLAGS = -DUTEST +CC = $(CCC) + +CSRC = acltest.cpp onetest.cpp ustubs.cpp twotest.cpp +TSRC = aclfile0 aclfile1 aclfile2 aclfile3 aclfile4 aclfile5 aclfile6 aclfile7 aclfile8 aclfile9 aclfile10 aclfile11 aclfile12 aclfile13 aclfile14 aclfile15 aclfile16 aclfile17 aclfile18 aclfile19 test.ref +SRC = $(CSRC) $(TSRC) +XSRC = \ + ../oneeval.cpp \ + ../lastod.cpp \ + ../lasip.cpp \ + ../aclutil.cpp \ + ../lasdns.cpp \ + ../acl.tab.cpp \ + ../acl.yy.cpp \ + ../acltools.cpp \ + ../aclspace.cpp \ + ../lasgroup.cpp \ + ../lasuser.cpp \ + ../lasprogram.cpp \ + ../nseframe.cpp \ + ../aclcache.cpp \ + ../register.cpp \ + ../symbols.cpp \ + ../method.cpp \ + ../authdb.cpp + +COBJ = $(CSRC:%.cpp=%.o) +XOBJ = $(XSRC:../%.cpp=%.o) + +# This may be needed for other platforms too +ifeq ($(ARCH), IRIX) +XLIBS = -rpath $(LDAP_LIBPATH) +endif + +ifeq ($(ARCH), HPUX) +XLIBS = -lpthread +endif +ifeq ($(ARCH), SOLARIS) +XLIBS = -lsocket -lnsl -ldl -lposix4 +endif + +XLIBS+= $(OBJDIR)/lib/base/plist.o \ + $(OBJDIR)/lib/base/pool.o \ + $(OBJDIR)/lib/base/util.o \ + $(OBJDIR)/lib/base/ereport.o \ + $(OBJDIR)/lib/base/system.o \ + $(OBJDIR)/lib/base/shexp.o \ + $(OBJDIR)/lib/base/pblock.o \ + $(OBJDIR)/lib/base/file.o \ + $(OBJDIR)/lib/base/systhr.o \ + $(OBJDIR)/lib/base/nscperror.o \ + $(OBJDIR)/lib/libldapu.a \ + $(LIBNSPR) + +all: $(COBJ) $(TSRC) acltest + ./acltest > test.out + diff test.ref test.out + @echo + @echo "The unit test is passed if there is no diff output, and the" + @echo "Purify window shows no errors and 0 bytes leaked." + @echo + @echo "Run - gmake coverage - manually to get code coverage analysis." + @echo + +aclparse: ustubs.o testmain.o $(XOBJ) + purify $(CC) -o aclparse testmain.o $(XOBJ) ustubs.o $(XLIBS) + +aclparse.pure: acl.tab.o acl.yy.o testmain.o acltools.o ustubs.o + purify -user-path=.. $(CC) -o aclparse.pure $(XOBJ) ustubs.o $(XLIBS) + +onetest: onetest.o ustubs.o $(XOBJ) + $(CC) -o onetest onetest.o $(XOBJ) ustubs.o $(XLIBS) + +twotest: twotest.o ustubs.o $(XOBJ) + $(CC) -o twotest twotest.o $(XOBJ) ustubs.o $(XLIBS) + +acltest: acltest.o ustubs.o $(XOBJ) +# purify $(CC) -o acltest acltest.o $(XOBJ) ustubs.o $(XLIBS) + $(CC) -o acltest acltest.o $(XOBJ) ustubs.o $(XLIBS) + +coverage: acltest.o ustubs.o $(XOBJ) + purecov $(CC) -o acltestcov acltest.o $(XOBJ) ustubs.o $(XLIBS) + rm -f *.pcv + acltestcov + +lasemail: lasemail.o + $(LD) -G -h lasemail.so -o lasemail.so lasemail.o + +#$(XOBJ): $(XSRC) +# cd ..; gmake OBJDEST=$(UTESTDEST) CC=$(OCC) TESTFLAGS=$(TESTFLAGS) + +%.o:../%.c + $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o + +%.o:../%.cpp + $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o diff --git a/lib/libaccess/utest/acl.dat b/lib/libaccess/utest/acl.dat new file mode 100644 index 00000000..d640adca --- /dev/null +++ b/lib/libaccess/utest/acl.dat @@ -0,0 +1,12 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; + +acl davids; + +deny (all) dns=aruba.mcom.com; diff --git a/lib/libaccess/utest/aclfile0 b/lib/libaccess/utest/aclfile0 new file mode 100644 index 00000000..8bb2a428 --- /dev/null +++ b/lib/libaccess/utest/aclfile0 @@ -0,0 +1,55 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile0; +deny with uri="test"; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); + +acl aclfile0.0; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.1; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.2; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.3; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.4; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.5; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.6; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.7; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.8; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.9; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.10; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.11; +allow (read, write, execute) (timeofday<2100); +acl aclfile0.12; +authenticate (user, group) { + database=franco; + method=basic; +}; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); diff --git a/lib/libaccess/utest/aclfile1 b/lib/libaccess/utest/aclfile1 new file mode 100644 index 00000000..e148f1a1 --- /dev/null +++ b/lib/libaccess/utest/aclfile1 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile1; +deny (read, write, execute) (timeofday<2100); +deny (read, write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile10 b/lib/libaccess/utest/aclfile10 new file mode 100644 index 00000000..f0f5a223 --- /dev/null +++ b/lib/libaccess/utest/aclfile10 @@ -0,0 +1,13 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile10; +deny absolute (read) ip="17.34.*"; +allow (read,write) timeofday>1700; +deny (read) dns="*.mcom.com"; +allow (read,write) dayofweek=mon; diff --git a/lib/libaccess/utest/aclfile11 b/lib/libaccess/utest/aclfile11 new file mode 100644 index 00000000..9fe73cb2 --- /dev/null +++ b/lib/libaccess/utest/aclfile11 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile11; +allow (read) (timeofday<2100); +allow (html_write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile12 b/lib/libaccess/utest/aclfile12 new file mode 100644 index 00000000..ac154d7a --- /dev/null +++ b/lib/libaccess/utest/aclfile12 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile12; +allow (read) (timeofday<2100); +allow (read, html_write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile13 b/lib/libaccess/utest/aclfile13 new file mode 100644 index 00000000..7334d03d --- /dev/null +++ b/lib/libaccess/utest/aclfile13 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile13; +allow (read) (ip="17.34.1.1+255.255.0.0"); +allow (html_write) (dns!="*.microsoft.com"); diff --git a/lib/libaccess/utest/aclfile14 b/lib/libaccess/utest/aclfile14 new file mode 100644 index 00000000..5fc5c706 --- /dev/null +++ b/lib/libaccess/utest/aclfile14 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile14; +allow (read, write) (ip="17.34.*"); +deny (write) (dns!="*.mcom.com"); diff --git a/lib/libaccess/utest/aclfile15 b/lib/libaccess/utest/aclfile15 new file mode 100644 index 00000000..2d8701ec --- /dev/null +++ b/lib/libaccess/utest/aclfile15 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile15; +allow (html_read, write) (dns="*.mcom.com"); +deny (read) (ip="17.34.*"); diff --git a/lib/libaccess/utest/aclfile16 b/lib/libaccess/utest/aclfile16 new file mode 100644 index 00000000..54ce99f8 --- /dev/null +++ b/lib/libaccess/utest/aclfile16 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile16; +allow (html_read, write) (dns="*.mcom.com"); +deny (read) (ip="17.34.1.1 + 255.255.0.0"); diff --git a/lib/libaccess/utest/aclfile17 b/lib/libaccess/utest/aclfile17 new file mode 100644 index 00000000..128076f3 --- /dev/null +++ b/lib/libaccess/utest/aclfile17 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile17; +allow absolute (all) (dns="*.mcom.com"); +deny (read) (ip="17.34.1.1+255.255.0.0"); diff --git a/lib/libaccess/utest/aclfile18 b/lib/libaccess/utest/aclfile18 new file mode 100644 index 00000000..4a80bc27 --- /dev/null +++ b/lib/libaccess/utest/aclfile18 @@ -0,0 +1,19 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile18; +authenticate (user) { + method=SSL; + database=LDAP; +}; +allow (read, write, execute, create) dns="*.mcom.com"; +authenticate (user) { + method=basic; + database=20; +}; +allow (read, write, execute, create) (timeofday>1700 or timeofday<0800); diff --git a/lib/libaccess/utest/aclfile19 b/lib/libaccess/utest/aclfile19 new file mode 100644 index 00000000..4433f4c7 --- /dev/null +++ b/lib/libaccess/utest/aclfile19 @@ -0,0 +1,14 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile19A; +deny (read, write, execute, create) dns!="*.mcom.com"; +allow absolute (read) ((timeofday>1700 or timeofday<0800) or dayofweek=satsunmon); + +acl aclfile19B; +deny (write) dns="*.mcom.com"; diff --git a/lib/libaccess/utest/aclfile2 b/lib/libaccess/utest/aclfile2 new file mode 100644 index 00000000..eee5c30c --- /dev/null +++ b/lib/libaccess/utest/aclfile2 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile2; +deny (read) (timeofday<2100); +deny (read) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile3 b/lib/libaccess/utest/aclfile3 new file mode 100644 index 00000000..094c1abe --- /dev/null +++ b/lib/libaccess/utest/aclfile3 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile3; +allow (read) (timeofday<2100); +allow (read) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile4 b/lib/libaccess/utest/aclfile4 new file mode 100644 index 00000000..befc7b4b --- /dev/null +++ b/lib/libaccess/utest/aclfile4 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile4; +allow (read) (timeofday>0700); +allow (write) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile5 b/lib/libaccess/utest/aclfile5 new file mode 100644 index 00000000..8b0e1e8d --- /dev/null +++ b/lib/libaccess/utest/aclfile5 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile5; +allow (read) (ip="17.34.*"); +allow (write) (dns!="*.microsoft.com"); diff --git a/lib/libaccess/utest/aclfile6 b/lib/libaccess/utest/aclfile6 new file mode 100644 index 00000000..9646b548 --- /dev/null +++ b/lib/libaccess/utest/aclfile6 @@ -0,0 +1,23 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile6; +allow (read, +write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +deny (write) +(dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); diff --git a/lib/libaccess/utest/aclfile7 b/lib/libaccess/utest/aclfile7 new file mode 100644 index 00000000..d8f9aa13 --- /dev/null +++ b/lib/libaccess/utest/aclfile7 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile7; +allow (read,write) (dns="*.mcom.com"); +deny (read) (ip="17.34.*"); diff --git a/lib/libaccess/utest/aclfile8 b/lib/libaccess/utest/aclfile8 new file mode 100644 index 00000000..b11cfe7e --- /dev/null +++ b/lib/libaccess/utest/aclfile8 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile8; +allow (read, write, execute, create) dns="*.mcom.com"; +allow (read, write, execute, create) (timeofday>1700 or timeofday<0800); diff --git a/lib/libaccess/utest/aclfile9 b/lib/libaccess/utest/aclfile9 new file mode 100644 index 00000000..2a0ab35e --- /dev/null +++ b/lib/libaccess/utest/aclfile9 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile9; +deny (read) ip="*.34.*+*.128.*.0"; +allow (read, write, execute, create) ((timeofday>1700 or timeofday<0800) or dayofweek=satsunmon); diff --git a/lib/libaccess/utest/aclgrp0 b/lib/libaccess/utest/aclgrp0 new file mode 100644 index 00000000..ba08fbf1 --- /dev/null +++ b/lib/libaccess/utest/aclgrp0 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp0; +allow (read, write, execute) (group = marketing or group!="Directory Administrators"); diff --git a/lib/libaccess/utest/aclgrp1 b/lib/libaccess/utest/aclgrp1 new file mode 100644 index 00000000..7a804404 --- /dev/null +++ b/lib/libaccess/utest/aclgrp1 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp1; +allow (read, write, execute) (group!="Directory Administrators"); diff --git a/lib/libaccess/utest/aclgrp2 b/lib/libaccess/utest/aclgrp2 new file mode 100644 index 00000000..13938c19 --- /dev/null +++ b/lib/libaccess/utest/aclgrp2 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp2; +allow (read, write, execute) (group=marketing); diff --git a/lib/libaccess/utest/aclgrp3 b/lib/libaccess/utest/aclgrp3 new file mode 100644 index 00000000..30b44d5c --- /dev/null +++ b/lib/libaccess/utest/aclgrp3 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp3; +allow (read, write, execute) (group>"Directory Admin,marketing"); diff --git a/lib/libaccess/utest/aclgrp4 b/lib/libaccess/utest/aclgrp4 new file mode 100644 index 00000000..5b07d6c2 --- /dev/null +++ b/lib/libaccess/utest/aclgrp4 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp4; +allow (read, write, execute) (group = " marketing ,, Directory Administrators ,, "); diff --git a/lib/libaccess/utest/acltest.cpp b/lib/libaccess/utest/acltest.cpp new file mode 100644 index 00000000..c643f873 --- /dev/null +++ b/lib/libaccess/utest/acltest.cpp @@ -0,0 +1,796 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <base/session.h> +#include <base/daemon.h> +#include <base/systhr.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include "../aclcache.h" +#include <libaccess/las.h> + + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); + +int +TestEvalFunc(NSErr_t *errp, char *attr, 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) +{ + return 0; +} + +void +TestFlushFunc(void **cookie) +{ + return; +} + +static int parse_dburl (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist, void **db) +{ + *db = strdup(url); + return 0; +} + + +main() +{ + ACLListHandle_t *acl_list; + int result; + ACLCachable_t cachable = 0; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[3]; + char filename[20]; + char newfilename[25]; + int i; + char *map_generic[7]; + LASEvalFunc_t Eval_funcp; + LASFlushFunc_t Flush_funcp; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + int ii; + char **name_list; + ACLMethod_t method=NULL; + ACLDbType_t dbtype=NULL; + int rv; + ACLAttrGetterList_t aglist; + ACLAttrGetter_t *agptr; + char **names; + int cnt; + + systhread_init("acl_utest"); + + char *acl_file_list[3] = {"aclfile6", "aclfile7", NULL}; + char *new_filename = "merge6_7"; + char *acl_name_list[3] = {"aclfile6", "aclfile7", NULL}; + char *new_aclname = "merge6_7"; + char *bad_acl_file_list[3] = {"bad_aclfile6", "bad_aclfile7", NULL}; + + if ( ACL_FileMergeFile(NULL, new_filename, bad_acl_file_list, 0) < 0 ) { + printf("Failed ACL_FileMergeFile() test.\n"); + } + + if ( ACL_FileMergeFile(NULL, new_filename, acl_file_list, 0) < 0 ) { + printf("Failed ACL_FileMergeFile() test.\n"); + } + + if ( ACL_FileMergeAcl(NULL, new_filename, acl_name_list, new_aclname, 0) < 0 ) { + printf("Failed ACL_FileMergeAcl() test.\n"); + } + + /* LAS Registration Unit Tests */ + + ACL_Init(); + + rv = ACL_MethodRegister(NULL, "one", &method); + printf("Method one is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "two", &method); + printf("Method two is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "one", &method); + printf("Method one repeated is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "three", &method); + printf("Method three is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodNamesGet(NULL, &names, &cnt); + + for(i = 0; i < cnt; i++) { + printf("\tMethod[%d] = \"%s\"\n", i, names[i]); + } + + ACL_MethodNamesFree(NULL, names, cnt); + + if (!ACL_MethodIsEqual(NULL, method, method)) { + printf("Error comparing methods"); + } + + if (!ACL_MethodNameIsEqual(NULL, method, "three")) { + printf("Error comparing method by name"); + } + + /* Since LDAP is already registered by ACL_Init, the first number + * we'll get is actually 2. + */ + rv = ACL_DbTypeRegister(NULL, "two", parse_dburl, &dbtype); + printf("DbType two is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "three", parse_dburl, &dbtype); + printf("DbType three is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "two", parse_dburl, &dbtype); + printf("DbType two repeated is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "four", parse_dburl, &dbtype); + printf("DbType four is #%d, rv=%d\n", (int)dbtype, rv); + + if (!ACL_DbTypeIsEqual(NULL, dbtype, dbtype)) { + printf("Error comparing dbtypes\n"); + } + + if (!ACL_DbTypeNameIsEqual(NULL, dbtype, "four")) { + printf("Error comparing dbtype by name\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db1", "url for db1", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db1\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db2", "url for db2", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db2\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db3", "url for db3", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db3\n"); + } + + rv = ACL_DatabaseNamesGet(NULL, &names, &cnt); + + for(i = 0; i < cnt; i++) { + printf("\tDatabase[%d] = \"%s\"\n", i, names[i]); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)2, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_FRONT, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)3, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_END, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)1, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_FRONT, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)4, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_END, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterFind(NULL, "attr", &aglist)) { + printf("Error finding attr getter\n"); + } + + for (i = 0, agptr = ACL_AttrGetterFirst(&aglist); + i < 4; + i++, agptr = ACL_AttrGetterNext(&aglist, agptr)) { + + if (agptr) { + printf("position %d\n", (int)(agptr->fn)); + } + else { + printf("***Error: missing getter ***\n"); + } + } + +#ifndef XP_WIN32 + if (ACL_LasRegister(NULL, "test_attr", TestEvalFunc, TestFlushFunc)) { + printf("Error registering Test LAS functions\n"); + } + ACL_LasFindEval(NULL, "test_attr", &Eval_funcp); + if (Eval_funcp != TestEvalFunc) { + printf("Error finding Eval function - expecting %x, got %x\n", + TestEvalFunc, Eval_funcp); + } + ACL_LasFindFlush(NULL, "test_attr", &Flush_funcp); + if (Flush_funcp != TestFlushFunc) { + printf("Error finding Flush function - expecting %x, got %x\n", + TestFlushFunc, Flush_funcp); + } + ACL_LasFindEval(NULL, "wrong_attr", &Eval_funcp); + if (Eval_funcp != NULL) { + printf("Error finding Eval function - expecting NULL, got %x\n", + Eval_funcp); + } + ACL_LasFindFlush(NULL, "wrong_attr", &Flush_funcp); + if (Flush_funcp != NULL) { + printf("Error finding Flush function - expecting NULL, got %x\n", + Flush_funcp); + } +#endif /* !XP_WIN32 */ + + /* ACL Eval Unit Tests + */ + rights[0] = "http_get"; + rights[1] = "http_post"; + rights[2] = NULL; + + eval.subject = NULL; + eval.resource = NULL; + + for (i=0; i<10; i++) { + sprintf(filename, "aclfile%d", i); + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, filename); + if ( eval.acllist == NULL ) { + printf("Couldn't parse.\n"); + continue; + } + + sprintf(newfilename, "%s.v30", filename); + if ( ACL_WriteFile(NULL, newfilename, eval.acllist) < 0) { + printf("Couldn't write %s.\n", newfilename); + } + result = ACL_EvalTestRights(NULL, &eval, &rights[0], + http_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", filename, result); + } + +/******************************************************************** + + TEST #1 + + TEST ACL_ParseString() + TEST ACL_WriteFile() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl > franco;"); + if ( acl_list != NULL ) { + ACL_ListDestroy(NULL, acl_list); + printf("Test #1a fails parsed invalid ACL\n"); + goto skip_test; + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \nallow (read) user=franco;"); + if ( acl_list == NULL ) { + printf("Test #1b fails couldn't parse valid ACL\n"); + goto skip_test; + } else { + if ( ACL_WriteFile(NULL, "buffer", acl_list) < 0) { + printf("Test #1b, couldn't write %s.\n", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \njunk (read) user=franco;"); + + if ( acl_list != NULL ) { + printf("Test #1c failed missed syntax error\n"); + ACL_ListDestroy(NULL, acl_list); + goto skip_test; + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \nallow (read) user=franco;"); + + if ( acl_list == NULL ) { + printf("Test #1d couldn't parse valid ACL\n"); + } else { + ACL_ListDestroy(NULL, acl_list); + goto skip_test; + } + + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #1e, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) == NULL ) { + printf("Test #1e, couldn't find %s in %s.\n", "franco", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #2 + + TEST ACL_FileDeleteAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileDeleteAcl(NULL, "buffer", "franco", ACL_CASE_INSENSITIVE) < 0) { + printf("Test #2, couldn't write %s.\n", "buffer"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #2, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) ) { + printf("Couldn't delete %s from %s.\n", "franco", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #3 + + TEST ACL_FileSetAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileSetAcl(NULL, "buffer", + "version 3.0; acl FileSetAcl; \nallow (read) user=franco;", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #3, couldn't ACL_FileSetACL(%s).\n", "FileSetAcl"); + } + if ( ACL_FileSetAcl(NULL, "buffer", + "version 3.0; acl franco; \nallow (read) user=franco;", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #3, couldn't ACL_FileSetACL(%s).\n", "franco"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #3, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #3, couldn't set %s in %s.\n", "franco", "buffer"); + } + if ( ACL_ListFind(NULL, acl_list, "filesetacl", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #3, couldn't set %s in %s.\n", "filesetacl", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #4 + + TEST ACL_FileRenameAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileRenameAcl(NULL, "buffer", "FileSetAcl", "loser", ACL_CASE_INSENSITIVE)< 0) { + printf("Test #4, fail ACL_FileRenameACL(filesetacl, loser).\n"); + } + if ( ACL_FileRenameAcl(NULL, "buffer", "franco", "bigdogs", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #4, fail ACL_FileRenameACL(franco, bigdogs).\n"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #3, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "loser", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #4, fail rename %s in %s.\n", "loser", "buffer"); + } + if ( ACL_ListFind(NULL, acl_list, "bigdogs", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #4, fail rename %s in %s.\n", "bigdogs", "buffer"); + } + if ( ACL_ListGetNameList(NULL, acl_list, &name_list) < 0 ) { + printf("Test #4, yikes, the GetNameList failed.\n"); + } else { + for (ii = 0; name_list[ii]; ii++) + printf("ACL %s\n", name_list[ii]); + ACL_NameListDestroy(NULL, name_list); + } + ACL_ListDestroy(NULL, acl_list); + } + + + + +skip_test: +/******************************************************************** + + END + +*********************************************************************/ + + rights[0] = "html_read"; + rights[1] = "html_write"; + + map_generic[0] = "html_read"; + map_generic[1] = "html_write"; + map_generic[2] = "N/A"; + map_generic[3] = "html_create"; + map_generic[4] = "html_delete"; + map_generic[5] = "N/A"; + map_generic[6] = NULL; + + for (i=10; i<20; i++) { + sprintf(filename, "aclfile%d", i); + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, filename); + if ( eval.acllist == NULL ) { + printf("Parse failed.\n"); + continue; + } + result = ACL_EvalTestRights(NULL, &eval, &rights[0], map_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", filename, result); + } + + /* + * Program LAS Unit Tests + */ + char *groups[32] = { + "http-foo", + "http-bar", + "http-grog", + NULL + }; + char *programs[32] = { + "foo, fubar, frobozz", + "bar, shoo, fly", + "grog, beer", + NULL + }; + struct program_groups program_groups; + program_groups.groups = groups; + program_groups.programs = programs; + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"foo", (PList_t)&program_groups, NULL, NULL); + printf("program = foo %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"nomatch", (PList_t)&program_groups, NULL, NULL); + printf("program = nomatch %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"beer", (PList_t)&program_groups, NULL, NULL); + printf("program = beer %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar, http-grog", &cachable, &las_cookie, (PList_t)"http-grog", (PList_t)&program_groups, NULL, NULL); + printf("program = http-grog %d\n\n", result); + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo", &cachable, &las_cookie, (PList_t)"ubar", (PList_t)&program_groups, NULL, NULL); + printf("program = ubar %d\n\n", result); + + + /* + * DNS LAS Unit Tests + */ + + result = LASDnsEval(NULL, "dnsalias", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dnsalias = *? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dnsalias", CMP_OP_EQ, "aruba.mcom.com brain251.mcom.com", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dnsalias = aruba.mcom.com brain251.mcom.com? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *? %d\n\n", result); + + result = LASDnsEval(NULL, "dns", CMP_OP_NE, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns != *? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "aruba.mcom.com", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = aruba.mcom.com? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "ai.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = ai.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.ai.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.ai.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_NE, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns != *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "mistake", CMP_OP_NE, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake != *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_GT, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns > *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + + /* + * IP LAS Unit Tests + */ + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = *? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != *? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "*.*.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = *.*.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.*.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.*.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.*+255.255.255.255", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.*+255.255.255.255? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69+255.255.255.254, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69+255.255.255.254, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "17.34.51.69+255.255.255.254, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 17.34.51.69+255.255.255.254, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68, 17.34.51.69", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68, 17.34.51.69? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68, 17.34.51.69, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68, 17.34.51.69, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "17.34.51.68, 17.34.51.69, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 17.34.51.68, 17.34.51.69, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69+255.255.255.254", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69+255.255.255.254? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.50.69+255.255.254.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.50.69+255.255.254.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.35.50.69+255.254.0.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.35.50.69+255.254.0.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "16.35.50.69+254.0.0.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 16.35.50.69+254.0.0.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_GT, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip > 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_LT, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip < 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_GE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip >= 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_LE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip <= 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "mistake", CMP_OP_LE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("mistake <= 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + + /* + * Time of Day unit tests. + */ + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "mistake", CMP_OP_LE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake <= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "0800-2200", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 0800-2200? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "0800-2200", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 0800-2200? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2200-0800? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2200-0800? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2200-0800? %d\n\n", result); + + + /* + * Day Of Week Unit Tests + */ + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "Mon", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= mon? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "tUe", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= tUe? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "weD", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= weD? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "THu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= THu? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "FrI", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= FrI? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "sAT", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= tUe? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "mon,tuewed,thu,frisatsun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= mon,tuewed,thu,frisatsun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_NE, "mon,tuewed,thu,frisatsun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("!= mon,tuewed,thu,frisatsun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_GT, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("> Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_LT, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("< Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_GE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf(">= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_LE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("<= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "mistake", CMP_OP_LE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake <= Sun? %d\n\n", result); + + + ACL_Destroy(); + + exit(0); + +} diff --git a/lib/libaccess/utest/lasemail.cpp b/lib/libaccess/utest/lasemail.cpp new file mode 100644 index 00000000..469a315f --- /dev/null +++ b/lib/libaccess/utest/lasemail.cpp @@ -0,0 +1,180 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasemail.cpp + * This file contains the Email LAS code. + */ + +#include <ldap.h> +#include <nsacl/aclapi.h> + +#define ACL_ATTR_EMAIL "email" + +extern "C" { +extern int LASEmailEval(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); +extern void LASEmailFlush(void **las_cookie); +extern int LASEmailModuleInit(); +} + + +/* + * LASEmailEval + * INPUT + * attr_name The string "email" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of emails + * (we currently support only one e-mail addr) + * *cachable Always set to ACL_NOT_CACHABLE. + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASEmailEval(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) +{ + char *uid; + char *email; + int rv; + LDAP *ld; + char *basedn; + LDAPMessage *res; + int numEntries; + char filter[1024]; + int matched; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_EMAIL) != 0) { + fprintf(stderr, "LASEmailEval called for incorrect attr \"%s\"\n", + attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + fprintf(stderr, "LASEmailEval called with incorrect comparator %d\n", + comparator); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* get the authenticated user name */ + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + /* We have an authenticated user */ + if (!strcmp(attr_pattern, "all")) { + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* do an ldap lookup for: (& (uid=<user>) (mail=<email>)) */ + rv = ACL_LDAPDatabaseHandle(errp, NULL, &ld, &basedn); + + if (rv != LAS_EVAL_TRUE) { + fprintf(stderr, "unable to get LDAP handle\n"); + return rv; + } + + /* Formulate the filter -- assume single e-mail in attr_pattern */ + /* If we support multiple comma separated e-mail addresses in the + * attr_pattern then the filter will look like: + * (& (uid=<user>) (| (mail=<email1>) (mail=<email2>))) + */ + sprintf(filter, "(& (uid=%s) (mail=%s))", uid, attr_pattern); + + rv = ldap_search_s(ld, basedn, LDAP_SCOPE_SUBTREE, filter, + 0, 0, &res); + + if (rv != LDAP_SUCCESS) + { + fprintf(stderr, "ldap_search_s: %s\n", ldap_err2string(rv)); + return LAS_EVAL_FAIL; + } + + numEntries = ldap_count_entries(ld, res); + + if (numEntries == 1) { + /* success */ + LDAPMessage *entry = ldap_first_entry(ld, res); + char *dn = ldap_get_dn(ld, entry); + + fprintf(stderr, "ldap_search_s: Entry found. DN: \"%s\"\n", dn); + ldap_memfree(dn); + matched = 1; + } + else if (numEntries == 0) { + /* not found -- but not an error */ + fprintf(stderr, "ldap_search_s: Entry not found. Filter: \"%s\"\n", + filter); + matched = 0; + } + else if (numEntries > 0) { + /* Found more than one entry! */ + fprintf(stderr, "ldap_search_s: Found more than one entry. Filter: \"%s\"\n", + filter); + return LAS_EVAL_FAIL; + } + + if (comparator == CMP_OP_EQ) { + rv = (matched ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + rv = (matched ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + return rv; +} + + +/* LASEmailFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASEmailFlush(void **las_cookie) +{ + /* do nothing */ + return; +} + +/* LASEmailModuleInit -- + * Register the e-mail LAS. + * + * To load this functions in the web server, compile the file in + * "lasemail.so" and add the following lines to the + * <ServerRoot>/https-<name>/config/obj.conf file. Be sure to change the + * "lasemail.so" portion to the full pathname. E.g. /nshome/lib/lasemail.so. + * + * Init fn="load-modules" funcs="LASEmailModuleInit" shlib="lasemail.so" + * Init fn="acl-register-module" module="lasemail" func="LASEmailModuleInit" + */ +int LASEmailModuleInit () +{ + NSErr_t err = NSERRINIT; + NSErr_t *errp = &err; + int rv; + + rv = ACL_LasRegister(errp, ACL_ATTR_EMAIL, LASEmailEval, LASEmailFlush); + + if (rv < 0) { + fprintf(stderr, "ACL_LasRegister failed. Error: %d\n", rv); + return rv; + } + + return rv; +} + diff --git a/lib/libaccess/utest/onetest.cpp b/lib/libaccess/utest/onetest.cpp new file mode 100644 index 00000000..3bcccbb1 --- /dev/null +++ b/lib/libaccess/utest/onetest.cpp @@ -0,0 +1,47 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <libaccess/nserror.h> +#include <base/session.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <base/plist.h> +#include <base/ereport.h> + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); +extern ACLEvalDestroyContext(NSErr_t *errp, ACLEvalHandle_t *acleval); + + +main(int arc, char **argv) +{ + int result; + int cachable; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[2]; + char filename[20]; + int i; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + + /* ACL Eval Unit Tests + */ + rights[0] = "read"; + rights[1] = "write"; + rights[2] = NULL; + + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, argv[1]); + result = ACL_EvalTestRights(NULL, &eval, &rights[0], NULL, &bong, &bong_type, &acl_tag, &expr_num); + ACLEvalDestroyContext(NULL, &eval); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", argv[1], result); + +} diff --git a/lib/libaccess/utest/shexp.cpp b/lib/libaccess/utest/shexp.cpp new file mode 100644 index 00000000..23e9e909 --- /dev/null +++ b/lib/libaccess/utest/shexp.cpp @@ -0,0 +1,294 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shexp.c: shell-like wildcard match routines + * + * + * See shexp.h for public documentation. + * + * Rob McCool + * + */ + +#include "shexp.h" +#include <ctype.h> /* isalpha, tolower */ + + +/* ----------------------------- shexp_valid ------------------------------ */ + + +int valid_subexp(char *exp, char stop) +{ + register int x,y,t; + int nsc,np,tld; + + x=0;nsc=0;tld=0; + + while(exp[x] && (exp[x] != stop)) { + switch(exp[x]) { + case '~': + if(tld) return INVALID_SXP; + else ++tld; + case '*': + case '?': + case '^': + case '$': + ++nsc; + break; + case '[': + ++nsc; + if((!exp[++x]) || (exp[x] == ']')) + return INVALID_SXP; + for(++x;exp[x] && (exp[x] != ']');++x) + if(exp[x] == '\\') + if(!exp[++x]) + return INVALID_SXP; + if(!exp[x]) + return INVALID_SXP; + break; + case '(': + ++nsc;np = 0; + while(1) { + if(exp[++x] == ')') + return INVALID_SXP; + for(y=x;(exp[y]) && (exp[y] != '|') && (exp[y] != ')');++y) + if(exp[y] == '\\') + if(!exp[++y]) + return INVALID_SXP; + if(!exp[y]) + return INVALID_SXP; + if(exp[y] == '|') + ++np; + t = valid_subexp(&exp[x],exp[y]); + if(t == INVALID_SXP) + return INVALID_SXP; + x+=t; + if(exp[x] == ')') { + if(!np) + return INVALID_SXP; + break; + } + } + break; + case ')': + case ']': + return INVALID_SXP; + case '\\': + if(!exp[++x]) + return INVALID_SXP; + default: + break; + } + ++x; + } + if((!stop) && (!nsc)) + return NON_SXP; + return ((exp[x] == stop) ? x : INVALID_SXP); +} + +NSAPI_PUBLIC int shexp_valid(char *exp) { + int x; + + x = valid_subexp(exp, '\0'); + return (x < 0 ? x : VALID_SXP); +} + + +/* ----------------------------- shexp_match ----------------------------- */ + + +#define MATCH 0 +#define NOMATCH 1 +#define ABORTED -1 + +int _shexp_match(char *str, char *exp); + +int handle_union(char *str, char *exp) +{ + char *e2 = (char *) MALLOC(sizeof(char)*strlen(exp)); + register int t,p2,p1 = 1; + int cp; + + while(1) { + for(cp=1;exp[cp] != ')';cp++) + if(exp[cp] == '\\') + ++cp; + for(p2 = 0;(exp[p1] != '|') && (p1 != cp);p1++,p2++) { + if(exp[p1] == '\\') + e2[p2++] = exp[p1++]; + e2[p2] = exp[p1]; + } + for(t=cp+1;(e2[p2] = exp[t]);++t,++p2); + if(_shexp_match(str,e2) == MATCH) { + FREE(e2); + return MATCH; + } + if(p1 == cp) { + FREE(e2); + return NOMATCH; + } + else ++p1; + } +} + + +int _shexp_match(char *str, char *exp) +{ + register int x,y; + int ret,neg; + + ret = 0; + for(x=0,y=0;exp[y];++y,++x) { + if((!str[x]) && (exp[y] != '(') && (exp[y] != '$') && (exp[y] != '*')) + ret = ABORTED; + else { + switch(exp[y]) { + case '$': + if( (str[x]) ) + ret = NOMATCH; + else + --x; /* we don't want loop to increment x */ + break; + case '*': + while(exp[++y] == '*'); + if(!exp[y]) + return MATCH; + while(str[x]) { + switch(_shexp_match(&str[x++],&exp[y])) { + case NOMATCH: + continue; + case ABORTED: + ret = ABORTED; + break; + default: + return MATCH; + } + break; + } + if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x])) + return MATCH; + else + ret = ABORTED; + break; + case '[': + if((neg = ((exp[++y] == '^') && (exp[y+1] != ']')))) + ++y; + + if((isalnum(exp[y])) && (exp[y+1] == '-') && + (isalnum(exp[y+2])) && (exp[y+3] == ']')) + { + int start = exp[y], end = exp[y+2]; + + /* Droolproofing for pinheads not included */ + if(neg ^ ((str[x] < start) || (str[x] > end))) { + ret = NOMATCH; + break; + } + y+=3; + } + else { + int matched; + + for(matched=0;exp[y] != ']';y++) + matched |= (str[x] == exp[y]); + if(neg ^ (!matched)) + ret = NOMATCH; + } + break; + case '(': + return handle_union(&str[x],&exp[y]); + break; + case '?': + break; + case '\\': + ++y; + default: +#ifdef XP_UNIX + if(str[x] != exp[y]) +#else /* XP_WIN32 */ + if(strnicmp(str + x, exp + y, 1)) +#endif /* XP_WIN32 */ + ret = NOMATCH; + break; + } + } + if(ret) + break; + } + return (ret ? ret : (str[x] ? NOMATCH : MATCH)); +} + +NSAPI_PUBLIC int shexp_match(char *str, char *xp) { + register int x; + char *exp = STRDUP(xp); + + for(x=strlen(exp)-1;x;--x) { + if((exp[x] == '~') && (exp[x-1] != '\\')) { + exp[x] = '\0'; + if(_shexp_match(str,&exp[++x]) == MATCH) + goto punt; + break; + } + } + if(_shexp_match(str,exp) == MATCH) { + FREE(exp); + return 0; + } + + punt: + FREE(exp); + return 1; +} + + +/* ------------------------------ shexp_cmp ------------------------------- */ + + +NSAPI_PUBLIC int shexp_cmp(char *str, char *exp) +{ + switch(shexp_valid(exp)) { + case INVALID_SXP: + return -1; + case NON_SXP: +#ifdef XP_UNIX + return (strcmp(exp,str) ? 1 : 0); +#else /* XP_WIN32 */ + return (stricmp(exp,str) ? 1 : 0); +#endif /* XP_WIN32 */ + default: + return shexp_match(str, exp); + } +} + + +/* ---------------------------- shexp_casecmp ----------------------------- */ + + +NSAPI_PUBLIC int shexp_casecmp(char *str, char *exp) +{ + char *lstr = STRDUP(str), *lexp = STRDUP(exp), *t; + int ret; + + for(t = lstr; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + for(t = lexp; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + + switch(shexp_valid(lexp)) { + case INVALID_SXP: + ret = -1; + break; + case NON_SXP: + ret = (strcmp(lexp, lstr) ? 1 : 0); + break; + default: + ret = shexp_match(lstr, lexp); + } + FREE(lstr); + FREE(lexp); + return ret; +} + diff --git a/lib/libaccess/utest/shexp.h b/lib/libaccess/utest/shexp.h new file mode 100644 index 00000000..edc91842 --- /dev/null +++ b/lib/libaccess/utest/shexp.h @@ -0,0 +1,131 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shexp.h: Defines and prototypes for shell exp. match routines + * + * + * This routine will match a string with a shell expression. The expressions + * accepted are based loosely on the expressions accepted by zsh. + * + * o * matches anything + * o ? matches one character + * o \ will escape a special character + * o $ matches the end of the string + * o [abc] matches one occurence of a, b, or c. The only character that needs + * to be escaped in this is ], all others are not special. + * o [a-z] matches any character between a and z + * o [^az] matches any character except a or z + * o ~ followed by another shell expression will remove any pattern + * matching the shell expression from the match list + * o (foo|bar) will match either the substring foo, or the substring bar. + * These can be shell expressions as well. + * + * The public interface to these routines is documented below. + * + * Rob McCool + * + */ + +#ifndef SHEXP_H +#define SHEXP_H + +/* + * Requires that the macro MALLOC be set to a "safe" malloc that will + * exit if no memory is available. If not under MCC httpd, define MALLOC + * to be the real malloc and play with fire, or make your own function. + */ + +#include "../netsite.h" + +#include <ctype.h> /* isalnum */ +#include <string.h> /* strlen */ + + +/* + * Wrappers for shexp/regexp + * + * Portions of code that explicitly want to have either shexp's + * or regexp's should call those functions directly. + * + * Common code bases for multiple products should use the following + * macros instead to use either shell or regular expressions, + * depending on the flavor chosen for a given server. + * + */ +#if defined(MCC_PROXY) && defined(USE_REGEX) + +#include "base/regexp.h" + +#define WILDPAT_VALID(exp) regexp_valid(exp) +#define WILDPAT_MATCH(str, exp) regexp_match(str, exp) +#define WILDPAT_CMP(str, exp) regexp_cmp(str, exp) +#define WILDPAT_CASECMP(str, exp) regexp_casecmp(str, exp) + +#else /* HTTP servers */ + +#define WILDPAT_VALID(exp) shexp_valid(exp) +#define WILDPAT_MATCH(str, exp) shexp_match(str, exp) +#define WILDPAT_CMP(str, exp) shexp_cmp(str, exp) +#define WILDPAT_CASECMP(str, exp) shexp_casecmp(str, exp) + +#endif + + +/* --------------------------- Public routines ---------------------------- */ + +NSPR_BEGIN_EXTERN_C + +/* + * shexp_valid takes a shell expression exp as input. It returns: + * + * NON_SXP if exp is a standard string + * INVALID_SXP if exp is a shell expression, but invalid + * VALID_SXP if exp is a valid shell expression + */ + +#define NON_SXP -1 +#define INVALID_SXP -2 +#define VALID_SXP 1 + +/* and generic shexp/regexp versions */ +#define NON_WILDPAT NON_SXP +#define INVALID_WILDPAT INVALID_SXP +#define VALID_WILDPAT VALID_SXP + +/* and regexp versions */ +#define NON_REGEXP NON_SXP +#define INVALID_REGEXP INVALID_SXP +#define VALID_REGEXP VALID_SXP + + +NSAPI_PUBLIC int shexp_valid(char *exp); + +/* + * shexp_match + * + * Takes a prevalidated shell expression exp, and a string str. + * + * Returns 0 on match and 1 on non-match. + */ + +NSAPI_PUBLIC int shexp_match(char *str, char *exp); + + +/* + * shexp_cmp + * + * Same as above, but validates the exp first. 0 on match, 1 on non-match, + * -1 on invalid exp. shexp_casecmp does the same thing but is case + * insensitive. + */ + +NSAPI_PUBLIC int shexp_cmp(char *str, char *exp); +NSAPI_PUBLIC int shexp_casecmp(char *str, char *exp); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/utest/test.ref b/lib/libaccess/utest/test.ref new file mode 100644 index 00000000..d5207382 --- /dev/null +++ b/lib/libaccess/utest/test.ref @@ -0,0 +1,234 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +Failed ACL_FileMergeFile() test. +Method one is #1, rv=0 +Method two is #2, rv=0 +Method one repeated is #1, rv=0 +Method three is #3, rv=0 + Method[0] = "two" + Method[1] = "three" + Method[2] = "one" +DbType two is #2, rv=0 +DbType three is #3, rv=0 +DbType two repeated is #2, rv=0 +DbType four is #4, rv=0 + Database[0] = "db2" + Database[1] = "db1" + Database[2] = "db3" +position 1 +position 2 +position 3 +position 4 +aclfile0 = 0 + +aclfile1 = 1 + +aclfile2 = 1 + +aclfile3 = 1 + +aclfile4 = 0 + +aclfile5 = 0 + +aclfile6 = 0 + +aclfile7 = 1 + +aclfile8 = 0 + +aclfile9 = 3 + +ACL file: internal-buffer +Syntax error at line: 1, token: > +ACL file: internal-buffer +Syntax error at line: 2, token: junk +aclfile10 = 1 + +aclfile11 = 1 + +aclfile12 = 0 + +aclfile13 = 0 + +aclfile14 = 0 + +aclfile15 = 1 + +aclfile16 = 1 + +aclfile17 = 0 + +aclfile18 = 0 + +aclfile19 = 1 + +program = foo -1 + +program = nomatch -2 + +program = beer -1 + +program = http-grog -2 + +program = ubar -2 + +dnsalias = *? -1 + +dnsalias = aruba.mcom.com brain251.mcom.com? -1 + +dns = *? -1 + +dns != *? -2 + +dns = aruba.mcom.com? -1 + +dns = ai.mit.edu? -2 + +dns = *.ai.mit.edu? -2 + +dns = *.mit.edu? -2 + +dns = *.edu? -2 + +dns != *.edu? -1 + +mistake != *.edu? -5 + +dns > *.edu? -5 + +ip = *? -1 + +ip != *? -2 + +ip = *.*.*.*? -1 + +ip = 17.*? -1 + +ip = 17.*.*.*? -1 + +ip = 17.34.*? -1 + +ip = 17.34.*.*? -1 + +ip = 17.34.51.*? -1 + +ip = 17.34.51.*+255.255.255.255? -1 + +ip = 17.34.51.69+255.255.255.254, 123.45.67.89? -1 + +ip != 17.34.51.69+255.255.255.254, 123.45.67.89? -2 + +ip = 17.34.51.68, 17.34.51.69? -1 + +ip = 17.34.51.68, 17.34.51.69, 123.45.67.89? -1 + +ip != 17.34.51.68, 17.34.51.69, 123.45.67.89? -2 + +ip = 17.34.51.68? -1 + +ip = 17.34.51.69? -2 + +ip = 17.34.51.69+255.255.255.254? -1 + +ip = 17.34.50.69+255.255.254.0? -1 + +ip = 17.35.50.69+255.254.0.0? -1 + +ip = 16.35.50.69+254.0.0.0? -1 + +ip = 123.45.67.89? -2 + +ip != 123.45.67.89? -1 + +ip > 123.45.67.89? -5 + +ip < 123.45.67.89? -5 + +ip >= 123.45.67.89? -5 + +ip <= 123.45.67.89? -5 + +mistake <= 123.45.67.89? -5 + +time = 2120? -1 + +time != 2120? -2 + +time = 0700? -2 + +time != 0700? -1 + +time = 2400? -2 + +time != 2400? -1 + +time > 2120? -2 + +time < 2120? -2 + +time > 0700? -1 + +time < 0700? -2 + +time > 2400? -2 + +time < 2400? -1 + +time >= 2120? -1 + +time <= 2120? -1 + +time >= 0700? -1 + +time <= 0700? -2 + +time >= 2400? -2 + +time <= 2400? -1 + +mistake <= 2400? -5 + +time = 0800-2200? -1 + +time != 0800-2200? -2 + +time = 2200-0800? -2 + +time != 2200-0800? -1 + +time <= 2200-0800? -5 + += mon? -1 + += tUe? -2 + += weD? -2 + += THu? -2 + += FrI? -2 + += tUe? -2 + += Sun? -2 + += mon,tuewed,thu,frisatsun? -1 + +!= mon,tuewed,thu,frisatsun? -2 + +> Sun? -5 + +< Sun? -5 + +>= Sun? -5 + +<= Sun? -5 + +mistake <= Sun? -5 + diff --git a/lib/libaccess/utest/testmain.cpp b/lib/libaccess/utest/testmain.cpp new file mode 100644 index 00000000..4da14cee --- /dev/null +++ b/lib/libaccess/utest/testmain.cpp @@ -0,0 +1,52 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * ACL parser unit test program + */ + +#include <stdio.h> +#include <netsite.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> + +main(int argc, char **argv) +{ + +ACLListHandle_t *acllist; +int ii; +char filename[255]; +ACLWrapper_t *wrap; +ACLExprHandle_t *expr; + + if ( argc < 2 ) { + fprintf(stderr, "usage: aclparse <filenames>\n"); + exit(1); + } + for (ii = 1; ii < argc; ii++ ) { + acllist = ACL_ParseFile(NULL, argv[ii]); + if ( acllist == NULL ) { + printf("Failed to parse ACL.\n"); + + } else { + for (wrap = acllist->acl_list_head; wrap; + wrap = wrap->wrap_next) { + for (expr=wrap->acl->expr_list_head; + expr; + expr = expr->expr_next ) { + ACL_ExprDisplay(expr); + } + } + } + + + sprintf(filename, "%s.v30", argv[ii]); + ACL_WriteFile(NULL, filename, acllist); + ACL_ListDestroy( acllist ); + } + +} diff --git a/lib/libaccess/utest/twotest.cpp b/lib/libaccess/utest/twotest.cpp new file mode 100644 index 00000000..2d4fb503 --- /dev/null +++ b/lib/libaccess/utest/twotest.cpp @@ -0,0 +1,57 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <base/session.h> +#include <base/plist.h> +#include <base/ereport.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> + + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); +extern ACLEvalDestroyContext(NSErr_t *errp, ACLEvalHandle_t *acleval); + +main(int arc, char **argv) +{ + int result; + int cachable; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[2]; + char *map_generic[7]; + char filename[20]; + int i; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + + /* ACL Eval Unit Tests + */ + + rights[0] = "html_read"; + rights[1] = "html_write"; + rights[2] = NULL; + + map_generic[0] = "html_read"; + map_generic[1] = "html_write"; + map_generic[2] = "N/A"; + map_generic[3] = "html_create"; + map_generic[4] = "html_delete"; + map_generic[5] = "N/A"; + map_generic[6] = NULL; + + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, argv[1]); + result = ACL_EvalTestRights(NULL, &eval, &rights[0], map_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACLEvalDestroyContext(NULL, &eval); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", argv[1], result); + +} diff --git a/lib/libaccess/utest/ustubs.cpp b/lib/libaccess/utest/ustubs.cpp new file mode 100644 index 00000000..ccfa3108 --- /dev/null +++ b/lib/libaccess/utest/ustubs.cpp @@ -0,0 +1,283 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <sys/types.h> +#include <malloc.h> +#include <string.h> +#include <base/crit.h> +#include <base/plist.h> + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/ldapacl.h> +#include <ldaputil/dbconf.h> +#ifdef NSPR20 +#include <prprf.h> +#else +#include <nspr/prprf.h> +#endif + +NSPR_BEGIN_EXTERN_C +extern char * ACL_Program; +extern int conf_getglobals(); +extern int SPconf_getglobals(); +extern int ereport(int, char*, ...); +extern int SPereport(int, char*, ...); +extern char * GetAdminLanguage(void); +extern char * XP_GetStringFromDatabase(char *strLibraryName, char *strLanguage, int iToken); +extern void ACL_Restart(void *cntlData); +extern int XP_SetError(); +extern int XP_GetError(); +extern int acl_usr_cache_init(); +extern int acl_usr_cache_set_group(); +extern int acl_usr_cache_group_check(); +extern int sema_destroy(); +extern char *ldapu_err2string(int err); +extern int ACL_CacheFlush(void); +NSPR_END_EXTERN_C + +static char errbuf[10]; + +char * +ldapu_err2string(int err) +{ + sprintf(errbuf, "%d", err); + return errbuf; +} + + +void init_ldb_rwlock () +{ +} + +sema_destroy() +{ + return 0; +} + +#ifdef notdef +char *system_errmsg() +{ + static char errmsg[1024]; + + sprintf(errmsg, "Stubbed system_errmsg"); + return errmsg; +} +#endif + +int +ACL_CacheFlushRegister(AclCacheFlushFunc_t flush_func) +{ + return 0; +} + +acl_usr_cache_init() +{ + return 0; +} + +acl_usr_cache_group_check() +{ + return 0; +} + +acl_usr_cache_set_group() +{ + return 0; +} + +XP_SetError() +{ + return 0; +} + +XP_GetError() +{ + return 0; +} + +CRITICAL +crit_init() +{ + return (CRITICAL)1; +} + +void +crit_enter(CRITICAL c) +{ + return; +} + +void +crit_exit(CRITICAL c) +{ + return; +} + +void +crit_terminate(CRITICAL c) +{ + return; +} + +int crit_owner_is_me(CRITICAL id) +{ + return 1; +} + +symTableFindSym() +{ + return 0; +} + +int +ldap_auth_uid_groupid(LDAP *ld, char *uid, char *groupid, + char *base) +{ + return 0; +} + +LDAP * +init_ldap (char *host, int port, int use_ssl) +{ + return (LDAP *)"init_ldap_stub"; +} + +int ACL_LDAPDatabaseHandle (NSErr_t *errp, const char *dbname, LDAP **ld, + char **basedn) +{ + *ld = (LDAP *)"ACL_LDAPDatabaseHandle_stub"; + if (basedn) *basedn = strdup("unknown basedn"); + return LAS_EVAL_TRUE; +} + +#ifdef notdef +NSEFrame_t * nserrGenerate(NSErr_t * errp, long retcode, long errorid, + char * program, int errc, ...) +{ + return 0; +} +#endif + +char * ACL_Program; + +char * +LASUserGetUser() +{ + return "hmiller"; +} + +LASIpGetIp() +{ + return(0x11223344); +} + +LASDnsGetDns(char **dnsv) +{ + *dnsv = "aruba.mcom.com"; + return 0; +} + +int +ACL_DestroyList() +{ +return(0); +} + +aclCheckHosts() +{ +return(0); +} + +aclCheckUsers() +{ +return(0); +} + +char *LASGroupGetUser() +{ + return("hmiller"); +} + +int +SPconf_getglobals() +{ + return 0; +} + +int +conf_getglobals() +{ + return 0; +} + +int +SPereport(int degree, char *fmt, ...) +{ + va_list args; + char errstr[1024]; + + va_start(args, fmt); + PR_vsnprintf(&errstr[0], sizeof(errstr), fmt, args); + printf("%s", errstr); + va_end(args); + return 0; +} + +int +ereport(int degree, char *fmt, ...) +{ + va_list args; + char errstr[1024]; + + va_start(args, fmt); + PR_vsnprintf(&errstr[0], sizeof(errstr), fmt, args); + printf("%s", errstr); + va_end(args); + return 0; +} + +#ifdef notdef +int dbconf_read_config_file (const char *file, DBConfInfo_t **conf_info_out) +{ + return 0; +} +#endif + +char * +GetAdminLanguage(void) +{ + return ""; +} + +static char errstr[1024]; + +char * +XP_GetStringFromDatabase(char *strLibraryName, char *strLanguage, int iToken) +{ + sprintf(errstr, "XP_GetAdminStr called for error %d\n", iToken); + return errstr; +} + +void +ACL_Restart(void * cntlData) +{ + return; +} + +NSAPI_PUBLIC int +parse_ldap_url(NSErr_t *errp, ACLDbType_t dbtype, const char *name, const char +*url, PList_t plist, void **db) +{ + return 0; +} + +int +ACL_CacheFlush(void) +{ + return 0; +} diff --git a/lib/libaccess/winnt.l b/lib/libaccess/winnt.l new file mode 100644 index 00000000..ce72b535 --- /dev/null +++ b/lib/libaccess/winnt.l @@ -0,0 +1,762 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +# define U(x) x +# define NLSTATE yyprevious=YYNEWLINE +# define BEGIN yybgin = yysvec + 1 + +# define INITIAL 0 +# define YYLERR yysvec +# define YYSTATE (yyestate-yysvec-1) +# define YYOPTIM 1 +# define YYLMAX BUFSIZ +#ifndef __cplusplus +# define output(c) (void)putc(c,yyout) +#else +# define lex_output(c) (void)putc(c,yyout) +#endif + +#if defined(__cplusplus) || defined(__STDC__) + +#if defined(__cplusplus) && defined(__EXTERN_C__) +extern "C" { +#endif + int yyback(int *, int); + int yyinput(void); + int yylook(void); + void yyoutput(int); + int yyracc(int); + int yyreject(void); + void yyunput(int); + int yylex(void); +#ifdef YYLEX_E + void yywoutput(wchar_t); + wchar_t yywinput(void); +#endif +#ifndef yyless + void yyless(int); +#endif +#ifndef yywrap + int yywrap(void); +#endif +#ifdef LEXDEBUG + void allprint(char); + void sprint(char *); +#endif +#if defined(__cplusplus) && defined(__EXTERN_C__) +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void exit(int); +#ifdef __cplusplus +} +#endif + +#endif +# define unput(c) {yytchar= (c);if(yytchar=='\n')yylineno--;*yysptr++=yytchar;} +# define yymore() (yymorfg=1) +#ifndef __cplusplus +# define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar) +#else +# define lex_input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar) +#endif +#define ECHO fprintf(yyout, "%s",yytext) +# define REJECT { nstr = yyreject(); goto yyfussy;} +int yyleng; extern char yytext[]; +int yymorfg; +extern char *yysptr, yysbuf[]; +int yytchar; +FILE *yyin = NULL, *yyout = NULL; +extern int yylineno; +struct yysvf { + struct yywork *yystoff; + struct yysvf *yyother; + int *yystops;}; +struct yysvf *yyestate; +extern struct yysvf yysvec[], *yybgin; + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "y.tab.h" +#include "libaccess/ava.h" +/*#include "netsite.h" */ + +int linenum = 1; +int first_time = 1; +int old_state; +int num_nested_comments = 0; + +extern AVAEntry tempEntry; +extern AVATable entryTable; + +void strip_quotes(void); + +# define COMMENT 2 +# define NORM 4 +# define DEFINES 6 +# define DEF_TYPE 8 +# define YYNEWLINE 10 +yylex(){ +int nstr; extern int yyprevious; + + if (yyin == NULL) yyin = stdin; + if (yyout == NULL) yyout = stdout; + if (first_time) { + BEGIN NORM; + first_time = tempEntry.numOrgs = 0; + old_state = NORM; + tempEntry.userid = 0; + tempEntry.country = 0; + tempEntry.CNEntry = 0; + tempEntry.email = 0; + tempEntry.locality = 0; + tempEntry.state = 0; + entryTable.numEntries = 0; + } +#ifdef __cplusplus +/* to avoid CC and lint complaining yyfussy not being used ...*/ +static int __lex_hack = 0; +if (__lex_hack) goto yyfussy; +#endif +while((nstr = yylook()) >= 0) +yyfussy: switch(nstr){ +case 0: +if(yywrap()) return(0); break; +case 1: + +# line 58 "avascan.l" + {BEGIN COMMENT; num_nested_comments++;} +break; +case 2: + +# line 59 "avascan.l" + {num_nested_comments--; + if (!num_nested_comments) BEGIN old_state;} +break; +case 3: + +# line 61 "avascan.l" + {;} +break; +case 4: + +# line 63 "avascan.l" + {yylval.string = system_strdup(yytext); + return USER_ID;} +break; +case 5: + +# line 65 "avascan.l" +{BEGIN DEF_TYPE; + old_state = DEF_TYPE;} +break; +case 6: + +# line 68 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_C; } +break; +case 7: + +# line 70 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_CO;} +break; +case 8: + +# line 72 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_OU;} +break; +case 9: + +# line 74 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_CN;} +break; +case 10: + +# line 76 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_L;} +break; +case 11: + +# line 78 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_E;} +break; +case 12: + +# line 80 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_ST;} +break; +case 13: + +# line 82 "avascan.l" + {BEGIN NORM;old_state = NORM;} +break; +case 14: + +# line 84 "avascan.l" + {return EQ_SIGN;} +break; +case 15: + +# line 85 "avascan.l" + {BEGIN DEF_TYPE; old_state = DEF_TYPE; + strip_quotes(); + return DEF_ID;} +break; +case 16: + +# line 89 "avascan.l" + {;} +break; +case 17: + +# line 90 "avascan.l" + {linenum++;} +break; +case 18: + +# line 91 "avascan.l" + {yyerror("Bad input character");} +break; +case -1: +break; +default: +(void)fprintf(yyout,"bad switch yylook %d",nstr); +} return(0); } +/* end of yylex */ + +int yywrap () { + return 1; +} + +void strip_quotes(void) { + yytext[strlen(yytext)-1]= '\0'; + yylval.string = system_strdup(&yytext[1]); +} +int yyvstop[] = { +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +18, +0, + +16, +18, +0, + +17, +0, + +18, +0, + +3, +18, +0, + +3, +16, +18, +0, + +3, +18, +0, + +3, +18, +0, + +4, +18, +0, + +18, +0, + +18, +0, + +14, +18, +0, + +6, +18, +0, + +11, +18, +0, + +10, +18, +0, + +7, +18, +0, + +18, +0, + +13, +18, +0, + +16, +0, + +1, +0, + +2, +0, + +4, +0, + +5, +0, + +15, +0, + +9, +0, + +8, +0, + +12, +0, +0}; +# define YYTYPE unsigned char +struct yywork { YYTYPE verify, advance; } yycrank[] = { +0,0, 0,0, 1,11, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 1,12, 1,13, +0,0, 3,15, 12,29, 0,0, +20,33, 0,0, 0,0, 0,0, +0,0, 3,16, 3,13, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 9,11, 0,0, 1,11, +0,0, 12,29, 7,21, 20,33, +8,21, 9,12, 9,13, 14,30, +0,0, 1,11, 3,15, 4,17, +1,14, 1,11, 2,14, 7,14, +4,18, 8,14, 3,17, 5,19, +3,15, 17,31, 5,14, 3,18, +3,15, 6,19, 10,14, 21,35, +6,14, 7,22, 9,11, 8,22, +0,0, 5,20, 0,0, 21,35, +21,35, 0,0, 0,0, 6,20, +9,11, 0,0, 0,0, 9,14, +9,11, 23,37, 10,23, 0,0, +10,24, 27,39, 26,38, 0,0, +0,0, 0,0, 0,0, 10,25, +0,0, 0,0, 10,26, 0,0, +21,36, 0,0, 10,27, 9,23, +0,0, 9,24, 0,0, 0,0, +0,0, 0,0, 21,35, 0,0, +9,25, 0,0, 21,35, 9,26, +0,0, 0,0, 0,0, 9,27, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 20,34, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 19,32, 0,0, 0,0, +10,28, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 9,28, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +0,0, 0,0, 0,0, 0,0, +19,32, 0,0, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +0,0}; +struct yysvf yysvec[] = { +0, 0, 0, +yycrank+-1, 0, yyvstop+1, +yycrank+-3, yysvec+1, yyvstop+3, +yycrank+-12, 0, yyvstop+5, +yycrank+-5, yysvec+3, yyvstop+7, +yycrank+-11, yysvec+1, yyvstop+9, +yycrank+-17, yysvec+1, yyvstop+11, +yycrank+-4, yysvec+1, yyvstop+13, +yycrank+-6, yysvec+1, yyvstop+15, +yycrank+-32, 0, yyvstop+17, +yycrank+-15, yysvec+9, yyvstop+19, +yycrank+0, 0, yyvstop+21, +yycrank+5, 0, yyvstop+23, +yycrank+0, 0, yyvstop+26, +yycrank+1, 0, yyvstop+28, +yycrank+0, 0, yyvstop+30, +yycrank+0, yysvec+12, yyvstop+33, +yycrank+10, 0, yyvstop+37, +yycrank+0, yysvec+14, yyvstop+40, +yycrank+93, 0, yyvstop+43, +yycrank+7, 0, yyvstop+46, +yycrank+-62, 0, yyvstop+48, +yycrank+0, 0, yyvstop+50, +yycrank+3, 0, yyvstop+53, +yycrank+0, 0, yyvstop+56, +yycrank+0, 0, yyvstop+59, +yycrank+1, 0, yyvstop+62, +yycrank+1, 0, yyvstop+65, +yycrank+0, 0, yyvstop+67, +yycrank+0, yysvec+12, yyvstop+70, +yycrank+0, 0, yyvstop+72, +yycrank+0, 0, yyvstop+74, +yycrank+0, yysvec+19, yyvstop+76, +yycrank+0, yysvec+20, 0, +yycrank+0, 0, yyvstop+78, +yycrank+0, yysvec+21, 0, +yycrank+0, 0, yyvstop+80, +yycrank+0, 0, yyvstop+82, +yycrank+0, 0, yyvstop+84, +yycrank+0, 0, yyvstop+86, +0, 0, 0}; +struct yywork *yytop = yycrank+215; +struct yysvf *yybgin = yysvec+1; +char yymatch[] = { + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 10, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9, 1, 34, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 1, 1, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 1, 1, 1, 1, 1, 1, + 1, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 1, 1, 1, 1, 44, + 1, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +0}; +char yyextra[] = { +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0}; +/* Copyright (c) 1989 AT&T */ +/* All Rights Reserved */ + +/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ +/* The copyright notice above does not evidence any */ +/* actual or intended publication of such source code. */ + +#pragma ident "@(#)ncform 6.7 93/06/07 SMI" + +int yylineno =1; +# define YYU(x) x +# define NLSTATE yyprevious=YYNEWLINE +char yytext[YYLMAX]; +struct yysvf *yylstate [YYLMAX], **yylsp, **yyolsp; +char yysbuf[YYLMAX]; +char *yysptr = yysbuf; +int *yyfnd; +extern struct yysvf *yyestate; +int yyprevious = YYNEWLINE; +#if defined(__cplusplus) || defined(__STDC__) +int yylook(void) +#else +yylook() +#endif +{ + register struct yysvf *yystate, **lsp; + register struct yywork *yyt; + struct yysvf *yyz; + int yych, yyfirst; + struct yywork *yyr; +# ifdef LEXDEBUG + int debug; +# endif + char *yylastch; + /* start off machines */ +# ifdef LEXDEBUG + debug = 0; +# endif + yyfirst=1; + if (!yymorfg) + yylastch = yytext; + else { + yymorfg=0; + yylastch = yytext+yyleng; + } + for(;;){ + lsp = yylstate; + yyestate = yystate = yybgin; + if (yyprevious==YYNEWLINE) yystate++; + for (;;){ +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"state %d\n",yystate-yysvec-1); +# endif + yyt = yystate->yystoff; + if(yyt == yycrank && !yyfirst){ /* may not be any transitions */ + yyz = yystate->yyother; + if(yyz == 0)break; + if(yyz->yystoff == yycrank)break; + } +#ifndef __cplusplus + *yylastch++ = yych = input(); +#else + *yylastch++ = yych = lex_input(); +#endif + if(yylastch > &yytext[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + yyfirst=0; + tryagain: +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"char "); + allprint(yych); + putchar('\n'); + } +# endif + yyr = yyt; + if ( (int)yyt > (int)yycrank){ + yyt = yyr + yych; + if (yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transitions */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + } +# ifdef YYOPTIM + else if((int)yyt < (int)yycrank) { /* r < yycrank */ + yyt = yyr = yycrank+(yycrank-yyt); +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"compressed state\n"); +# endif + yyt = yyt + yych; + if(yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transitions */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + yyt = yyr + YYU(yymatch[yych]); +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"try fall back character "); + allprint(YYU(yymatch[yych])); + putchar('\n'); + } +# endif + if(yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transition */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + } + if ((yystate = yystate->yyother) && (yyt= yystate->yystoff) != yycrank){ +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"fall back to state %d\n",yystate-yysvec-1); +# endif + goto tryagain; + } +# endif + else + {unput(*--yylastch);break;} + contin: +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"state %d char ",yystate-yysvec-1); + allprint(yych); + putchar('\n'); + } +# endif + ; + } +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"stopped at %d with ",*(lsp-1)-yysvec-1); + allprint(yych); + putchar('\n'); + } +# endif + while (lsp-- > yylstate){ + *yylastch-- = 0; + if (*lsp != 0 && (yyfnd= (*lsp)->yystops) && *yyfnd > 0){ + yyolsp = lsp; + if(yyextra[*yyfnd]){ /* must backup */ + while(yyback((*lsp)->yystops,-*yyfnd) != 1 && lsp > yylstate){ + lsp--; + unput(*yylastch--); + } + } + yyprevious = YYU(*yylastch); + yylsp = lsp; + yyleng = yylastch-yytext+1; + yytext[yyleng] = 0; +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"\nmatch "); + sprint(yytext); + fprintf(yyout," action %d\n",*yyfnd); + } +# endif + return(*yyfnd++); + } + unput(*yylastch); + } + if (yytext[0] == 0 /* && feof(yyin) */) + { + yysptr=yysbuf; + return(0); + } +#ifndef __cplusplus + yyprevious = yytext[0] = input(); + if (yyprevious>0) + output(yyprevious); +#else + yyprevious = yytext[0] = lex_input(); + if (yyprevious>0) + lex_output(yyprevious); +#endif + yylastch=yytext; +# ifdef LEXDEBUG + if(debug)putchar('\n'); +# endif + } + } +#if defined(__cplusplus) || defined(__STDC__) +int yyback(int *p, int m) +#else +yyback(p, m) + int *p; +#endif +{ + if (p==0) return(0); + while (*p) { + if (*p++ == m) + return(1); + } + return(0); +} + /* the following are only used in the lex library */ +#if defined(__cplusplus) || defined(__STDC__) +int yyinput(void) +#else +yyinput() +#endif +{ +#ifndef __cplusplus + return(input()); +#else + return(lex_input()); +#endif + } +#if defined(__cplusplus) || defined(__STDC__) +void yyoutput(int c) +#else +yyoutput(c) + int c; +#endif +{ +#ifndef __cplusplus + output(c); +#else + lex_output(c); +#endif + } +#if defined(__cplusplus) || defined(__STDC__) +void yyunput(int c) +#else +yyunput(c) + int c; +#endif +{ + unput(c); + } diff --git a/lib/libaccess/winnt.v b/lib/libaccess/winnt.v new file mode 100644 index 00000000..9fea3453 --- /dev/null +++ b/lib/libaccess/winnt.v @@ -0,0 +1,156 @@ +/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+#ifndef _VALUES_H
+#define _VALUES_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These values work with any binary representation of integers
+ * where the high-order bit contains the sign.
+ */
+
+/* a number used normally for size of a shift */
+#define BITSPERBYTE 8
+
+#define BITS(type) (BITSPERBYTE * (int)sizeof (type))
+
+/* short, regular and long ints with only the high-order bit turned on */
+#define HIBITS ((short)(1 << BITS(short) - 1))
+
+#if defined(__STDC__)
+
+#define HIBITI (1U << BITS(int) - 1)
+#define HIBITL (1UL << BITS(long) - 1)
+
+#else
+
+#define HIBITI ((unsigned)1 << BITS(int) - 1)
+#define HIBITL (1L << BITS(long) - 1)
+
+#endif
+
+/* largest short, regular and long int */
+#define MAXSHORT ((short)~HIBITS)
+#define MAXINT ((int)(~HIBITI))
+#define MAXLONG ((long)(~HIBITL))
+
+/*
+ * various values that describe the binary floating-point representation
+ * _EXPBASE - the exponent base
+ * DMAXEXP - the maximum exponent of a double (as returned by frexp())
+ * FMAXEXP - the maximum exponent of a float (as returned by frexp())
+ * DMINEXP - the minimum exponent of a double (as returned by frexp())
+ * FMINEXP - the minimum exponent of a float (as returned by frexp())
+ * MAXDOUBLE - the largest double
+ * ((_EXPBASE ** DMAXEXP) * (1 - (_EXPBASE ** -DSIGNIF)))
+ * MAXFLOAT - the largest float
+ * ((_EXPBASE ** FMAXEXP) * (1 - (_EXPBASE ** -FSIGNIF)))
+ * MINDOUBLE - the smallest double (_EXPBASE ** (DMINEXP - 1))
+ * MINFLOAT - the smallest float (_EXPBASE ** (FMINEXP - 1))
+ * DSIGNIF - the number of significant bits in a double
+ * FSIGNIF - the number of significant bits in a float
+ * DMAXPOWTWO - the largest power of two exactly representable as a double
+ * FMAXPOWTWO - the largest power of two exactly representable as a float
+ * _IEEE - 1 if IEEE standard representation is used
+ * _DEXPLEN - the number of bits for the exponent of a double
+ * _FEXPLEN - the number of bits for the exponent of a float
+ * _HIDDENBIT - 1 if high-significance bit of mantissa is implicit
+ * LN_MAXDOUBLE - the natural log of the largest double -- log(MAXDOUBLE)
+ * LN_MINDOUBLE - the natural log of the smallest double -- log(MINDOUBLE)
+ * LN_MAXFLOAT - the natural log of the largest float -- log(MAXFLOAT)
+ * LN_MINFLOAT - the natural log of the smallest float -- log(MINFLOAT)
+ */
+
+#if defined(__STDC__)
+
+/*
+ * Note that the following construct, "!#machine(name)", is a non-standard
+ * extension to ANSI-C. It is maintained here to provide compatibility
+ * for existing compilations systems, but should be viewed as transitional
+ * and may be removed in a future release. If it is required that this
+ * file not contain this extension, edit this file to remove the offending
+ * condition.
+ *
+ * These machines are all IEEE-754:
+ */
+#if #machine(i386) || defined(__i386) || #machine(sparc) || defined(__sparc)
+#define MAXDOUBLE 1.79769313486231570e+308
+#define MAXFLOAT ((float)3.40282346638528860e+38)
+#define MINDOUBLE 4.94065645841246544e-324
+#define MINFLOAT ((float)1.40129846432481707e-45)
+#define _IEEE 1
+#define _DEXPLEN 11
+#define _HIDDENBIT 1
+#define _LENBASE 1
+#define DMINEXP (-(DMAXEXP + DSIGNIF - _HIDDENBIT - 3))
+#define FMINEXP (-(FMAXEXP + FSIGNIF - _HIDDENBIT - 3))
+#else
+#error ISA not supported
+#endif
+
+#else
+
+/*
+ * These machines are all IEEE-754:
+ */
+#if defined(i386) || defined(__i386) || defined(sparc) || defined(__sparc)
+#define MAXDOUBLE 1.79769313486231570e+308
+#define MAXFLOAT ((float)3.40282346638528860e+38)
+#define MINDOUBLE 4.94065645841246544e-324
+#define MINFLOAT ((float)1.40129846432481707e-45)
+#define _IEEE 1
+#define _DEXPLEN 11
+#define _HIDDENBIT 1
+#define _LENBASE 1
+#define DMINEXP (-(DMAXEXP + DSIGNIF - _HIDDENBIT - 3))
+#define FMINEXP (-(FMAXEXP + FSIGNIF - _HIDDENBIT - 3))
+#else
+/* #error is strictly ansi-C, but works as well as anything for K&R systems. */
+/*#error ISA not supported */
+#endif
+
+#endif /* __STDC__ */
+
+#define _EXPBASE (1 << _LENBASE)
+#define _FEXPLEN 8
+#define DSIGNIF (BITS(double) - _DEXPLEN + _HIDDENBIT - 1)
+#define FSIGNIF (BITS(float) - _FEXPLEN + _HIDDENBIT - 1)
+#define DMAXPOWTWO ((double)(1L << BITS(long) - 2) * \
+ (1L << DSIGNIF - BITS(long) + 1))
+#define FMAXPOWTWO ((float)(1L << FSIGNIF - 1))
+#define DMAXEXP ((1 << _DEXPLEN - 1) - 1 + _IEEE)
+#define FMAXEXP ((1 << _FEXPLEN - 1) - 1 + _IEEE)
+#define LN_MAXDOUBLE (M_LN2 * DMAXEXP)
+#define LN_MAXFLOAT (float)(M_LN2 * FMAXEXP)
+#define LN_MINDOUBLE (M_LN2 * (DMINEXP - 1))
+#define LN_MINFLOAT (float)(M_LN2 * (FMINEXP - 1))
+#define H_PREC (DSIGNIF % 2 ? (1L << DSIGNIF/2) * M_SQRT2 : 1L << DSIGNIF/2)
+#define FH_PREC \
+ (float)(FSIGNIF % 2 ? (1L << FSIGNIF/2) * M_SQRT2 : 1L << FSIGNIF/2)
+#define X_EPS (1.0/H_PREC)
+#define FX_EPS (float)((float)1.0/FH_PREC)
+#define X_PLOSS ((double)(long)(M_PI * H_PREC))
+#define FX_PLOSS ((float)(long)(M_PI * FH_PREC))
+#define X_TLOSS (M_PI * DMAXPOWTWO)
+#define FX_TLOSS (float)(M_PI * FMAXPOWTWO)
+#define M_LN2 0.69314718055994530942
+#define M_PI 3.14159265358979323846
+#define M_SQRT2 1.41421356237309504880
+#define MAXBEXP DMAXEXP /* for backward compatibility */
+#define MINBEXP DMINEXP /* for backward compatibility */
+#define MAXPOWTWO DMAXPOWTWO /* for backward compatibility */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VALUES_H */
diff --git a/lib/libaccess/winnt.y b/lib/libaccess/winnt.y new file mode 100644 index 00000000..0ac06dfd --- /dev/null +++ b/lib/libaccess/winnt.y @@ -0,0 +1,793 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "libaccess/ava.h" +/*#include "libaccess/avapfile.h" */ +/* #include "netsite.h" */ + +extern char *currFile; + +extern int linenum; +extern char yytext[]; + +static void AddDefType (int defType, char *defId); +static void AddAVA (char* userID); + +void yyerror(const char* string); +extern void logerror(const char* string,int num, char *file); + +AVAEntry tempEntry; +AVATable entryTable; + + +typedef union +#ifdef __cplusplus + YYSTYPE +#endif + { + char *string; + int num; +} YYSTYPE; +# define DEF_C 257 +# define DEF_CO 258 +# define DEF_OU 259 +# define DEF_CN 260 +# define EQ_SIGN 261 +# define DEF_START 262 +# define DEF_L 263 +# define DEF_E 264 +# define DEF_ST 265 +# define USER_ID 266 +# define DEF_ID 267 + +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#else +#include <malloc.h> +#include <memory.h> +#endif + +#include <values.h> + +#ifdef __cplusplus + +#ifndef yyerror + void yyerror(const char *); +#endif + +#ifndef yylex +#ifdef __EXTERN_C__ + extern "C" { int yylex(void); } +#else + int yylex(void); +#endif +#endif + int yyparse(void); + +#endif +#define yyclearin yychar = -1 +#define yyerrok yyerrflag = 0 +extern int yychar; +extern int yyerrflag; +YYSTYPE yylval; +YYSTYPE yyval; +typedef int yytabelem; +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 150 +#endif +#if YYMAXDEPTH > 0 +int yy_yys[YYMAXDEPTH], *yys = yy_yys; +YYSTYPE yy_yyv[YYMAXDEPTH], *yyv = yy_yyv; +#else /* user does initial allocation */ +int *yys; +YYSTYPE *yyv; +#endif +static int yymaxdepth = YYMAXDEPTH; +# define YYERRCODE 256 + + + +void yyerror(const char* string) { + logerror(string,linenum,currFile); +} + + +void AddDefType (int defType, char *defId) { + switch (defType) { + case DEF_C: + tempEntry.country = defId; + break; + case DEF_CO: + tempEntry.company = defId; + break; + case DEF_OU: + if (tempEntry.numOrgs % ORGS_ALLOCSIZE == 0) { + if (tempEntry.numOrgs == 0) { + tempEntry.organizations = + system_malloc_perm (sizeof (char*) * ORGS_ALLOCSIZE); + } else { + char **temp; + temp = + system_malloc_perm(sizeof(char*) * (tempEntry.numOrgs + ORGS_ALLOCSIZE)); + memcpy (temp, tempEntry.organizations, + sizeof(char*)*tempEntry.numOrgs); + system_free_perm(tempEntry.organizations); + tempEntry.organizations = temp; + } + } + tempEntry.organizations[tempEntry.numOrgs++] = defId; + break; + case DEF_CN: + tempEntry.CNEntry = defId; + break; + case DEF_E: + tempEntry.email = defId; + break; + case DEF_L: + tempEntry.locality = defId; + break; + case DEF_ST: + tempEntry.state = defId; + break; + default: + break; + } +} + +void AddAVA (char* userID) { + AVAEntry *newAVA; + + newAVA = (AVAEntry*)system_malloc_perm(sizeof(AVAEntry)); + if (!newAVA) { + yyerror ("Out of Memory in AddAVA"); + return; + } + *newAVA = tempEntry; + newAVA->userid = userID; + + _addAVAtoTable (newAVA, &entryTable); + + tempEntry.CNEntry = tempEntry.userid = tempEntry.country = tempEntry.company = 0; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + tempEntry.numOrgs = 0; +} +yytabelem yyexca[] ={ +-1, 1, + 0, -1, + -2, 0, + }; +# define YYNPROD 18 +# define YYLAST 19 +yytabelem yyact[]={ + + 10, 11, 12, 13, 19, 4, 14, 15, 16, 18, + 8, 3, 7, 6, 5, 2, 1, 9, 17 }; +yytabelem yypact[]={ + + -261,-10000000, -261,-10000000, -257,-10000000,-10000000, -257,-10000000, -252, +-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000, -263,-10000000 }; +yytabelem yypgo[]={ + + 0, 17, 16, 15, 11, 13, 12, 10 }; +yytabelem yyr1[]={ + + 0, 2, 2, 3, 3, 4, 5, 5, 6, 6, + 7, 1, 1, 1, 1, 1, 1, 1 }; +yytabelem yyr2[]={ + + 0, 2, 0, 4, 2, 5, 2, 0, 4, 2, + 7, 3, 3, 3, 3, 3, 3, 3 }; +yytabelem yychk[]={ + +-10000000, -2, -3, -4, 266, -4, -5, -6, -7, -1, + 257, 258, 259, 260, 263, 264, 265, -7, 261, 267 }; +yytabelem yydef[]={ + + 2, -2, 1, 4, 7, 3, 5, 6, 9, 0, + 11, 12, 13, 14, 15, 16, 17, 8, 0, 10 }; +typedef struct +#ifdef __cplusplus + yytoktype +#endif +{ char *t_name; int t_val; } yytoktype; +#ifndef YYDEBUG +# define YYDEBUG 0 /* don't allow debugging */ +#endif + +#if YYDEBUG + +yytoktype yytoks[] = +{ + "DEF_C", 257, + "DEF_CO", 258, + "DEF_OU", 259, + "DEF_CN", 260, + "EQ_SIGN", 261, + "DEF_START", 262, + "DEF_L", 263, + "DEF_E", 264, + "DEF_ST", 265, + "USER_ID", 266, + "DEF_ID", 267, + "-unknown-", -1 /* ends search */ +}; + +char * yyreds[] = +{ + "-no such reduction-", + "source : ava.database", + "source : /* empty */", + "ava.database : ava.database ava", + "ava.database : ava", + "ava : USER_ID definitions", + "definitions : definition.list", + "definitions : /* empty */", + "definition.list : definition.list definition", + "definition.list : definition", + "definition : def.type EQ_SIGN DEF_ID", + "def.type : DEF_C", + "def.type : DEF_CO", + "def.type : DEF_OU", + "def.type : DEF_CN", + "def.type : DEF_L", + "def.type : DEF_E", + "def.type : DEF_ST", +}; +#endif /* YYDEBUG */ + + +/* +** Skeleton parser driver for yacc output +*/ + +/* +** yacc user known macros and defines +*/ +#define YYERROR goto yyerrlab +#define YYACCEPT return(0) +#define YYABORT return(1) +#define YYBACKUP( newtoken, newvalue )\ +{\ + if ( yychar >= 0 || ( yyr2[ yytmp ] >> 1 ) != 1 )\ + {\ + yyerror( "syntax error - cannot backup" );\ + goto yyerrlab;\ + }\ + yychar = newtoken;\ + yystate = *yyps;\ + yylval = newvalue;\ + goto yynewstate;\ +} +#define YYRECOVERING() (!!yyerrflag) +#define YYNEW(type) system_malloc(sizeof(type) * yynewmax) +#define YYCOPY(to, from, type) \ + (type *) memcpy(to, (char *) from, yynewmax * sizeof(type)) +#define YYENLARGE( from, type) \ + (type *) system_realloc((char *) from, yynewmax * sizeof(type)) +#ifndef YYDEBUG +# define YYDEBUG 1 /* make debugging available */ +#endif + +/* +** user known globals +*/ +int yydebug; /* set to 1 to get debugging */ + +/* +** driver internal defines +*/ +#define YYFLAG (-10000000) + +/* +** global variables used by the parser +*/ +YYSTYPE *yypv; /* top of value stack */ +int *yyps; /* top of state stack */ + +int yystate; /* current state */ +int yytmp; /* extra var (lasts between blocks) */ + +int yynerrs; /* number of errors */ +int yyerrflag; /* error recovery flag */ +int yychar; /* current input token number */ + + + +#ifdef YYNMBCHARS +#define YYLEX() yycvtok(yylex()) +/* +** yycvtok - return a token if i is a wchar_t value that exceeds 255. +** If i<255, i itself is the token. If i>255 but the neither +** of the 30th or 31st bit is on, i is already a token. +*/ +#if defined(__STDC__) || defined(__cplusplus) +int yycvtok(int i) +#else +int yycvtok(i) int i; +#endif +{ + int first = 0; + int last = YYNMBCHARS - 1; + int mid; + wchar_t j; + + if(i&0x60000000){/*Must convert to a token. */ + if( yymbchars[last].character < i ){ + return i;/*Giving up*/ + } + while ((last>=first)&&(first>=0)) {/*Binary search loop*/ + mid = (first+last)/2; + j = yymbchars[mid].character; + if( j==i ){/*Found*/ + return yymbchars[mid].tvalue; + }else if( j<i ){ + first = mid + 1; + }else{ + last = mid -1; + } + } + /*No entry in the table.*/ + return i;/* Giving up.*/ + }else{/* i is already a token. */ + return i; + } +} +#else/*!YYNMBCHARS*/ +#define YYLEX() yylex() +#endif/*!YYNMBCHARS*/ + +/* +** yyparse - return 0 if worked, 1 if syntax error not recovered from +*/ +#if defined(__STDC__) || defined(__cplusplus) +int yyparse(void) +#else +int yyparse() +#endif +{ + register YYSTYPE *yypvt; /* top of value stack for $vars */ + +#if defined(__cplusplus) || defined(lint) +/* + hacks to please C++ and lint - goto's inside switch should never be + executed; yypvt is set to 0 to avoid "used before set" warning. +*/ + static int __yaccpar_lint_hack__ = 0; + switch (__yaccpar_lint_hack__) + { + case 1: goto yyerrlab; + case 2: goto yynewstate; + } + yypvt = 0; +#endif + + /* + ** Initialize externals - yyparse may be called more than once + */ + yypv = &yyv[-1]; + yyps = &yys[-1]; + yystate = 0; + yytmp = 0; + yynerrs = 0; + yyerrflag = 0; + yychar = -1; + +#if YYMAXDEPTH <= 0 + if (yymaxdepth <= 0) + { + if ((yymaxdepth = YYEXPAND(0)) <= 0) + { + yyerror("yacc initialization error"); + YYABORT; + } + } +#endif + + { + register YYSTYPE *yy_pv; /* top of value stack */ + register int *yy_ps; /* top of state stack */ + register int yy_state; /* current state */ + register int yy_n; /* internal state number info */ + goto yystack; /* moved from 6 lines above to here to please C++ */ + + /* + ** get globals into registers. + ** branch to here only if YYBACKUP was called. + */ + yynewstate: + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + goto yy_newstate; + + /* + ** get globals into registers. + ** either we just started, or we just finished a reduction + */ + yystack: + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + + /* + ** top of for (;;) loop while no reductions done + */ + yy_stack: + /* + ** put a state and value onto the stacks + */ +#if YYDEBUG + /* + ** if debugging, look up token value in list of value vs. + ** name pairs. 0 and negative (-1) are special values. + ** Note: linear search is used since time is not a real + ** consideration while debugging. + */ + if ( yydebug ) + { + register int yy_i; + + printf( "State %d, token ", yy_state ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val == yychar ) + break; + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( ++yy_ps >= &yys[ yymaxdepth ] ) /* room on stack? */ + { + /* + ** reallocate and recover. Note that pointers + ** have to be reset, or bad things will happen + */ + int yyps_index = (yy_ps - yys); + int yypv_index = (yy_pv - yyv); + int yypvt_index = (yypvt - yyv); + int yynewmax; +#ifdef YYEXPAND + yynewmax = YYEXPAND(yymaxdepth); +#else + yynewmax = 2 * yymaxdepth; /* double table size */ + if (yymaxdepth == YYMAXDEPTH) /* first time growth */ + { + char *newyys = (char *)YYNEW(int); + char *newyyv = (char *)YYNEW(YYSTYPE); + if (newyys != 0 && newyyv != 0) + { + yys = YYCOPY(newyys, yys, int); + yyv = YYCOPY(newyyv, yyv, YYSTYPE); + } + else + yynewmax = 0; /* failed */ + } + else /* not first time */ + { + yys = YYENLARGE(yys, int); + yyv = YYENLARGE(yyv, YYSTYPE); + if (yys == 0 || yyv == 0) + yynewmax = 0; /* failed */ + } +#endif + if (yynewmax <= yymaxdepth) /* tables not expanded */ + { + yyerror( "yacc stack overflow" ); + YYABORT; + } + yymaxdepth = yynewmax; + + yy_ps = yys + yyps_index; + yy_pv = yyv + yypv_index; + yypvt = yyv + yypvt_index; + } + *yy_ps = yy_state; + *++yy_pv = yyval; + + /* + ** we have a new state - find out what to do + */ + yy_newstate: + if ( ( yy_n = yypact[ yy_state ] ) <= YYFLAG ) + goto yydefault; /* simple state */ +#if YYDEBUG + /* + ** if debugging, need to mark whether new token grabbed + */ + yytmp = yychar < 0; +#endif + if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) ) + yychar = 0; /* reached EOF */ +#if YYDEBUG + if ( yydebug && yytmp ) + { + register int yy_i; + + printf( "Received token " ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val == yychar ) + break; + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( ( ( yy_n += yychar ) < 0 ) || ( yy_n >= YYLAST ) ) + goto yydefault; + if ( yychk[ yy_n = yyact[ yy_n ] ] == yychar ) /*valid shift*/ + { + yychar = -1; + yyval = yylval; + yy_state = yy_n; + if ( yyerrflag > 0 ) + yyerrflag--; + goto yy_stack; + } + + yydefault: + if ( ( yy_n = yydef[ yy_state ] ) == -2 ) + { +#if YYDEBUG + yytmp = yychar < 0; +#endif + if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) ) + yychar = 0; /* reached EOF */ +#if YYDEBUG + if ( yydebug && yytmp ) + { + register int yy_i; + + printf( "Received token " ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; + yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val + == yychar ) + { + break; + } + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + /* + ** look through exception table + */ + { + register int *yyxi = yyexca; + + while ( ( *yyxi != -1 ) || + ( yyxi[1] != yy_state ) ) + { + yyxi += 2; + } + while ( ( *(yyxi += 2) >= 0 ) && + ( *yyxi != yychar ) ) + ; + if ( ( yy_n = yyxi[1] ) < 0 ) + YYACCEPT; + } + } + + /* + ** check for syntax error + */ + if ( yy_n == 0 ) /* have an error */ + { + /* no worry about speed here! */ + switch ( yyerrflag ) + { + case 0: /* new error */ + yyerror( "syntax error" ); + goto skip_init; + yyerrlab: + /* + ** get globals into registers. + ** we have a user generated syntax type error + */ + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + skip_init: + yynerrs++; + /* FALLTHRU */ + case 1: + case 2: /* incompletely recovered error */ + /* try again... */ + yyerrflag = 3; + /* + ** find state where "error" is a legal + ** shift action + */ + while ( yy_ps >= yys ) + { + yy_n = yypact[ *yy_ps ] + YYERRCODE; + if ( yy_n >= 0 && yy_n < YYLAST && + yychk[yyact[yy_n]] == YYERRCODE) { + /* + ** simulate shift of "error" + */ + yy_state = yyact[ yy_n ]; + goto yy_stack; + } + /* + ** current state has no shift on + ** "error", pop stack + */ +#if YYDEBUG +# define _POP_ "Error recovery pops state %d, uncovers state %d\n" + if ( yydebug ) + printf( _POP_, *yy_ps, + yy_ps[-1] ); +# undef _POP_ +#endif + yy_ps--; + yy_pv--; + } + /* + ** there is no state on stack with "error" as + ** a valid shift. give up. + */ + YYABORT; + case 3: /* no shift yet; eat a token */ +#if YYDEBUG + /* + ** if debugging, look up token in list of + ** pairs. 0 and negative shouldn't occur, + ** but since timing doesn't matter when + ** debugging, it doesn't hurt to leave the + ** tests here. + */ + if ( yydebug ) + { + register int yy_i; + + printf( "Error recovery discards " ); + if ( yychar == 0 ) + printf( "token end-of-file\n" ); + else if ( yychar < 0 ) + printf( "token -none-\n" ); + else + { + for ( yy_i = 0; + yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val + == yychar ) + { + break; + } + } + printf( "token %s\n", + yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( yychar == 0 ) /* reached EOF. quit */ + YYABORT; + yychar = -1; + goto yy_newstate; + } + }/* end if ( yy_n == 0 ) */ + /* + ** reduction by production yy_n + ** put stack tops, etc. so things right after switch + */ +#if YYDEBUG + /* + ** if debugging, print the string that is the user's + ** specification of the reduction which is just about + ** to be done. + */ + if ( yydebug ) + printf( "Reduce by (%d) \"%s\"\n", + yy_n, yyreds[ yy_n ] ); +#endif + yytmp = yy_n; /* value to switch over */ + yypvt = yy_pv; /* $vars top of value stack */ + /* + ** Look in goto table for next state + ** Sorry about using yy_state here as temporary + ** register variable, but why not, if it works... + ** If yyr2[ yy_n ] doesn't have the low order bit + ** set, then there is no action to be done for + ** this reduction. So, no saving & unsaving of + ** registers done. The only difference between the + ** code just after the if and the body of the if is + ** the goto yy_stack in the body. This way the test + ** can be made before the choice of what to do is needed. + */ + { + /* length of production doubled with extra bit */ + register int yy_len = yyr2[ yy_n ]; + + if ( !( yy_len & 01 ) ) + { + yy_len >>= 1; + yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */ + yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] + + *( yy_ps -= yy_len ) + 1; + if ( yy_state >= YYLAST || + yychk[ yy_state = + yyact[ yy_state ] ] != -yy_n ) + { + yy_state = yyact[ yypgo[ yy_n ] ]; + } + goto yy_stack; + } + yy_len >>= 1; + yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */ + yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] + + *( yy_ps -= yy_len ) + 1; + if ( yy_state >= YYLAST || + yychk[ yy_state = yyact[ yy_state ] ] != -yy_n ) + { + yy_state = yyact[ yypgo[ yy_n ] ]; + } + } + /* save until reenter driver code */ + yystate = yy_state; + yyps = yy_ps; + yypv = yy_pv; + } + /* + ** code supplied by user is placed in this switch + */ + switch( yytmp ) + { + +case 5: +{AddAVA(yypvt[-1].string);} break; +case 10: +{AddDefType(yypvt[-2].num, yypvt[-0].string);} break; +case 11: +{yyval.num = DEF_C; } break; +case 12: +{yyval.num = DEF_CO;} break; +case 13: +{yyval.num = DEF_OU;} break; +case 14: +{yyval.num = DEF_CN;} break; +case 15: +{yyval.num = DEF_L; } break; +case 16: +{yyval.num = DEF_E; } break; +case 17: +{yyval.num = DEF_ST;} break; + } + goto yystack; /* reset registers in driver code */ +} + diff --git a/lib/libaccess/wintab.h b/lib/libaccess/wintab.h new file mode 100644 index 00000000..b764cf78 --- /dev/null +++ b/lib/libaccess/wintab.h @@ -0,0 +1,26 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef union +#ifdef __cplusplus + YYSTYPE +#endif + { + char *string; + int num; +} YYSTYPE; +extern YYSTYPE yylval; +# define DEF_C 257 +# define DEF_CO 258 +# define DEF_OU 259 +# define DEF_CN 260 +# define EQ_SIGN 261 +# define DEF_START 262 +# define DEF_L 263 +# define DEF_E 264 +# define DEF_ST 265 +# define USER_ID 266 +# define DEF_ID 267 diff --git a/lib/libaccess/yy-sed b/lib/libaccess/yy-sed new file mode 100644 index 00000000..24c4eaac --- /dev/null +++ b/lib/libaccess/yy-sed @@ -0,0 +1,21 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +/#include <unistd.h>/d +/#include <values.h>/d +/#pragma ident/d +s/malloc.h/netsite.h/g +s/yyparse/acl_Parse/g +s/yy_flex_alloc/ACL_FLEX_ALLOC/g +s/yy_flex_realloc/ACL_FLEX_REALLOC/g +s/yy_flex_free/ACL_FLEX_FREE/g +s/malloc(/PERM_MALLOC(/g +s/free(/PERM_FREE(/g +s/realloc(/PERM_REALLOC(/g +s/yy/acl/g +s/YY/ACL/g +s/lex.acl.c/acl.yy.cpp/g diff --git a/lib/libadmin/Makefile b/lib/libadmin/Makefile new file mode 100644 index 00000000..68029ce2 --- /dev/null +++ b/lib/libadmin/Makefile @@ -0,0 +1,61 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# The admin libraries + +MCOM_ROOT=../../.. + +MODULE=LibAdmin +MODULE_CFLAGS=-DENCRYPT_PASSWORDS -DUSE_ADMSERV + +include ../../nsdefs.mk + +OBJDEST=$(OBJDIR)/lib/libadmin + +ifeq ($(ARCH), WINNT) +LIBS=$(OBJDIR)/lib/libadmin.lib +else +LIBS=$(OBJDIR)/lib/libadmin.a +endif + +OBJS=$(addprefix $(OBJDEST)/, admconf.o form_get.o error.o admlog.o \ + magconf.o ns-util.o objconf.o password.o \ + referer.o template.o util.o \ + hinstall.o admserv.o install.o nsnews.o \ + commit.o pcontrol.o get_msg.o \ + multconf.o httpcon.o authdb.o usrlists.o \ + dstats.o backup.o cluster.o \ + keyconf.o strlist.o $(OSOBJS)) + +# moved files [to libadminutil] : form_post.o strlist.o distadm.o cron_conf.o +# candidate for moing: admlog.o error.o admserv.o +# replaced files: error.o [by libadminutil/errRpt.c] +# removed for lack of ndbm support from binary release of libdbm.... userdb.o +#!! nsnews.o does not work anymore. + +all: $(OBJDEST) $(LIBS) + +$(LIBS): $(addprefix $(MCOM_ROOT)/ldapserver/include/libadmin/, \ + hadm_msgs.i la_msgs.i) + +include ../../nsconfig.mk + +MCC_INCLUDE += $(ADMINUTIL_INCLUDE) + +ifeq ($(ARCH), HPUX) +CC=$(CCC) +endif + +$(OBJDEST): + mkdir -p $(OBJDEST) + +$(LIBS): $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ + +include $(INCLUDE_DEPENDS) diff --git a/lib/libadmin/authdb.c b/lib/libadmin/authdb.c new file mode 100644 index 00000000..8aff4ca8 --- /dev/null +++ b/lib/libadmin/authdb.c @@ -0,0 +1,2467 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * authdb.c: Functions to aid in user/group database admin + * + * These things leak memory like a sieve. + * + * Ben Polk + * (blame Mike McCool for functions with an MLM) + */ + +#ifdef XP_UNIX +#include <dirent.h> +#endif /* WIN32? */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include "base/shexp.h" +#include "base/util.h" +#include "libadminutil/admutil.h" +#include "libadmin/libadmin.h" + +#include "libaccess/nsgmgmt.h" +#include "libaccess/nsumgmt.h" +/* XXX MLM - This shouldn't have to define itself as private. */ +#define __PRIVATE_NSADB +#include "libaccess/nsadb.h" +#include "libaccess/nsamgmt.h" +#include "libaccess/aclerror.h" +#include "libaccess/aclbuild.h" +#include "libaccess/acladmin.h" +#include "usrlists.h" + +#define BUF_SIZE 10 + +void list_authdbs(char *fullpath, char *partialpath); + +static char **list; +static int listsize; +static int curentry; + +/* + * Rights we know about. This should be moved out to some + * external location. Perhaps obj.conf? + */ +NSAPI_PUBLIC char *acl_read_rights[] = +{ + "GET", + "HEAD", + "POST", + "INDEX", +#ifdef MCC_PROXY + "CONNECT", +#endif + NULL +}; + +NSAPI_PUBLIC char *acl_write_rights[] = +{ + "PUT", + "DELETE", + "MKDIR", + "RMDIR", + "MOVE", + NULL +}; + + +/* + * passfilter - function returns non-zero if the regular expression + * passed in matches the string passed in. + */ +static int passfilter(char *str, char *regexp) +{ + if (!str) + return 0; /* NULL string never matches */ + else if (!regexp) + return 1; /* NULL regexp matches everything */ + else + return(!shexp_casecmp(str, regexp)); +} + +NSAPI_PUBLIC char **list_auth_dbs(char *fullpath) +{ + list = new_strlist(BUF_SIZE); + listsize = BUF_SIZE; + curentry = 0; + list_authdbs(fullpath, ""); + + return(list); +} + +NSAPI_PUBLIC void output_authdb_selector(char *path, char *element, char *current) { + char **pathlist = list_auth_dbs(path); + int currentnum = -1; + int plen = path ? strlen(path) : 0; + register int x; + + /* + * If the 'current' string begins with the 'path' string, remove the + * 'path' prefix. + */ + if (!strncmp(current, path, plen)) { + current += plen; + if (*current == FILE_PATHSEP) ++current; + } + + if (pathlist[0]) /* Is there at least one database? */ + { + /* Find current selection in list of databases. */ + for(x=0; pathlist[x]; x++) { + if(!strcmp(current, pathlist[x])) { + currentnum = x; + continue; + } + } + +/* BONEHEAD */ + fprintf(stdout, "<SELECT name=\"%s\" %s>", + element, (x>SELECT_OVERFLOW) ? "size=5" : ""); + + /* If the current selection is in there, put it first. */ + if(currentnum != -1) { + fprintf(stdout, "<OPTION value=\"%s\" SELECTED>%s\n", + pathlist[currentnum], pathlist[currentnum]); + + } + for(x=0; pathlist[x]; x++) { + if (x == currentnum) + continue; + + fprintf(stdout, "<OPTION value=\"%s\">%s\n", + pathlist[x], pathlist[x]); + } + fprintf(stdout, "</SELECT>"); + } else { + fprintf(stdout, "<b>No databases found.</b>"); + } +} + +NSAPI_PUBLIC char *get_current_authdb() +{ + char **config = get_adm_config(); + return(STRDUP(config[3])); +} + +NSAPI_PUBLIC void set_current_authdb(char *current) +{ + char **config = get_adm_config(); + config[3] = STRDUP(current); + write_adm_config(config); +} + +void list_authdbs(char *fullpath, char *partialpath) +{ + int stat_good; + struct stat finfo; + char **dirlisting; + char *path = (char *)MALLOC(strlen(fullpath)+strlen(partialpath)+2); + + sprintf(path, "%s%c%s", fullpath, FILE_PATHSEP, partialpath); + if( !(dirlisting = list_directory(path,0))) + return; + else { + register int x; + char *entry, *newppath; + + for(x=0; dirlisting[x]; x++) { + entry = (char *)MALLOC(strlen(path)+strlen(dirlisting[x])+2); + sprintf(entry, "%s%s", path, dirlisting[x]); + +#ifdef XP_UNIX + stat_good = (lstat(entry, &finfo) == -1 ? 0 : 1); +#else /* WIN32 */ + stat_good = (stat(entry, &finfo) == -1 ? 0 : 1); +#endif /* XP_UNIX */ + + if(!stat_good) + continue; + newppath = (char *)MALLOC(strlen(partialpath)+strlen(dirlisting[x])+3); + + if(S_ISDIR(finfo.st_mode)) { + sprintf(newppath, "%s%s", partialpath, dirlisting[x]); + curentry++; + + if(!(curentry < listsize)) { + listsize += BUF_SIZE; + list = grow_strlist(list, listsize); + } + list[curentry-1] = STRDUP(newppath); + list[curentry] = NULL; + } + + FREE(entry); + FREE(newppath); + } + } +} + +/* Get the userdb directory. (V1.x) */ +NSAPI_PUBLIC char *get_userdb_dir(void) +{ + char *userdb; + char line[BIG_LINE]; + +#ifdef USE_ADMSERV + char *tmp = getenv("NETSITE_ROOT"); + + sprintf(line, "%s%cuserdb", tmp, FILE_PATHSEP); +#else + char *tmp = get_mag_var("#ServerRoot"); + + sprintf(line, "%s%cadmin%cuserdb", tmp, FILE_PATHSEP, FILE_PATHSEP); +#endif + userdb = STRDUP(line); + return userdb; +} + +/* Get the httpacl directory. (V2.x) */ +NSAPI_PUBLIC char *get_httpacl_dir(void) +{ + char *httpacl; + char line[BIG_LINE]; + +#ifdef USE_ADMSERV + char *tmp = getenv("NETSITE_ROOT"); + + sprintf(line, "%s%chttpacl", tmp, FILE_PATHSEP); +#else + char *tmp = get_mag_var("#ServerRoot"); + + sprintf(line, "%s%cadmin%chttpacl", tmp, FILE_PATHSEP, FILE_PATHSEP); +#endif + httpacl = STRDUP(line); + return httpacl; +} + +/* Get the authdb directory. (V2.x) */ +NSAPI_PUBLIC char *get_authdb_dir(void) +{ + char *authdb; + char line[BIG_LINE]; + +#ifdef USE_ADMSERV + char *tmp = getenv("NETSITE_ROOT"); + + sprintf(line, "%s%cauthdb", tmp, FILE_PATHSEP); +#else + char *tmp = get_mag_var("#ServerRoot"); + + sprintf(line, "%s%cadmin%cauthdb", tmp, FILE_PATHSEP, FILE_PATHSEP); +#endif + authdb = STRDUP(line); + return authdb; +} +/* + * groupOrUser - function sets its return variable flags + * based on whether the name passed in is + * a user or a group in the specified database. + * It could be both, although the entry form + * are intended to prohibit this. + * Returns: 0 if no error occurs, something else otherwise. + */ +NSAPI_PUBLIC int groupOrUser(char *db_path, char *name, int *is_user, int *is_group) +{ + int rv = 1; + UserObj_t *uoptr; + GroupObj_t *goptr; + void *padb; + + if (name && is_user && is_group) { + *is_user = 0; + *is_group = 0; + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (!rv) { + + rv = nsadbFindByName(NULL, padb, name, AIF_USER, (void **)&uoptr); + if (rv == AIF_USER) { + *is_user = 1; + } + + rv = nsadbFindByName(NULL, padb, name, AIF_GROUP, (void **)&goptr); + if (rv == AIF_GROUP) { + *is_group = 1; + } + + nsadbClose(padb, 0); + } + } + + return rv; +} + +/* + * getfullname - function to get the fullname of a user. + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int getfullname(char *db_path, char *user, char **fullname) { + int rv; + UserObj_t *uoptr; + void *padb; + + if (db_path && user) + { + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv == 0) { + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (rv == AIF_USER) { + *fullname = (uoptr->uo_rname != 0) ? STRDUP((char *)uoptr->uo_rname) + : STRDUP(""); + } + else + rv = 1; + nsadbClose(padb, 0); + } + } + else + rv = 1; + + return rv; +} + +/* + * setfullname - function to set the fullname for the specified user. + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int setfullname(char *db_path, char *user, char *fullname) { + int rv; + UserObj_t *uoptr; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to update " + "the user's fullname in the database."); + } else { + /* See if the user already exists, if so, update it. */ + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (rv == AIF_USER) { + uoptr->uo_rname = (NTS_t)fullname; + } else { + /* User doesn't exist, so we've failed. */ + report_error(SYSTEM_ERROR, user, + "Unable to change this user's fullname, " + "user was not found in the database."); + rv = 1; + } + + if (uoptr) { + rv = nsadbModifyUser(NULL, padb, uoptr); + if (rv < 0) + report_error(SYSTEM_ERROR, user, + "A database error occurred while " + "trying to change the user fullname."); + } + nsadbClose(padb, 0); + } + return rv; +} + +/* Set a user's login name MLM*/ +NSAPI_PUBLIC int setusername(char *db_path, char *user, char *newname) { + int rv; + UserObj_t *uoptr; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to update " + "the user's fullname in the database."); + } else { + /* See if the user already exists, if so, update it. */ + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (rv != AIF_USER) { + /* User doesn't exist, so we've failed. */ + report_error(SYSTEM_ERROR, user, + "Unable to change this user's fullname, " + "user was not found in the database."); + rv = 1; + } + if (uoptr) { + rv = userRename(NULL, ((AuthDB_t *)padb)->adb_userdb, + uoptr, (NTS_t)newname); + + if (rv < 0) + report_error(SYSTEM_ERROR, user, + "A database error occurred while " + "trying to change the login name."); + } + nsadbClose(padb, 0); + } + return rv; +} + +/* + * addusertogroup - function to add a user to a group + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int addusertogroup(char *db_path, char *user, char *group) { + int rv; + UserObj_t *uoptr; + GroupObj_t *goptr; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to add " + "user to a group."); + } else { + /* See if the user and group exist. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (goptr == 0) { + report_error(INCORRECT_USAGE, group, + "The group was not found."); + } + else if (uoptr == 0) { + report_error(INCORRECT_USAGE, user, + "The user was not found."); + } + else { + rv = nsadbAddUserToGroup(NULL, padb, goptr, uoptr); + } + + nsadbClose(padb, 0); + } + return rv; +} + +/* + * addgrouptogroup - function to add a group to a group + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int addgrouptogroup(char *db_path, char *memgroup, char *group) { + int rv; + GroupObj_t *goptr; + GroupObj_t *mem_goptr; + void *padb; + + if (!strcmp(memgroup, group)) { + report_error(INCORRECT_USAGE, group, + "You can't add a group to itself."); + } + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to add " + "group to a group."); + } else { + /* See if the groups exist. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + rv = nsadbFindByName(NULL, + padb, memgroup, AIF_GROUP, (void **)&mem_goptr); + if (goptr == 0) { + report_error(INCORRECT_USAGE, group, + "The target group was not found."); + } + else if (mem_goptr == 0) { + report_error(INCORRECT_USAGE, memgroup, + "The group to add was not found."); + } + else { + rv = nsadbAddGroupToGroup(NULL, padb, goptr, mem_goptr); + } + + nsadbClose(padb, 0); + } + return rv; +} + +/* + * remuserfromgroup - function to remove a user from a group + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int remuserfromgroup(char *db_path, char *user, char *group) { + int rv; + UserObj_t *uoptr; + GroupObj_t *goptr; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to add " + "user to a group."); + } else { + /* See if the user and group exist. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (goptr == 0) { + report_error(SYSTEM_ERROR, group, + "The group was not found."); + } + else if (uoptr == 0) { + report_error(SYSTEM_ERROR, user, + "The user was not found."); + } + else { + rv = nsadbRemUserFromGroup(NULL, padb, goptr, uoptr); + if (rv) + report_error(SYSTEM_ERROR, "Error taking out user", + "An error occured trying to take " + "the user out of the group."); + } + + nsadbClose(padb, 0); + } + return rv; +} + +/* + * remgroupfromgroup - function to remove a group to a group + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int remgroupfromgroup(char *db_path, char *memgroup, char *group) { + int rv; + GroupObj_t *goptr; + GroupObj_t *mem_goptr; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to remove " + "a group from a group."); + } else { + /* See if the groups exist. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + rv = nsadbFindByName(NULL, + padb, memgroup, AIF_GROUP, (void **)&mem_goptr); + if (goptr == 0) { + report_error(SYSTEM_ERROR, group, + "The target group was not found."); + } + else if (mem_goptr == 0) { + report_error(SYSTEM_ERROR, memgroup, + "The group to remove was not found."); + } + else { + rv = nsadbRemGroupFromGroup(NULL, padb, goptr, mem_goptr); + } + + nsadbClose(padb, 0); + } + return rv; +} + +/* + * setpw - function to set the password for the specified user. + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int setpw(char *db_path, char *user, char *pwd) { + int rv; + UserObj_t *uoptr = 0; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to add " + "the password to the database."); + } else { + /* See if the user already exists, if so, update it. */ + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (uoptr != 0) { + uoptr->uo_pwd = (NTS_t)STRDUP(pw_enc(pwd)); + } else { + /* User doesn't exist, so we've failed. */ + report_error(SYSTEM_ERROR, user, + "Unable to change this user's password, " + "user was not found in the database."); + rv = 1; + } + + if (uoptr) { + rv = nsadbModifyUser(NULL, padb, uoptr); + if (rv < 0) + report_error(SYSTEM_ERROR, user, + "A database error occurred while " + "trying to change the user password."); + } + nsadbClose(padb, 0); + } + return rv; +} + +/* + * setdbpw - function to set the password on the special user + * who's password is used as the database password. + * If the password passed in is NULL, the user is + * removed or not created. + * If the password is not NULL, then the user will + * be created if needed, and it's password set to + * the one passed in. + * + * Return: Returns 0 if it works, something else if it fails. + */ +NSAPI_PUBLIC int setdbpw(char *db_path, char *pwd) +{ + int rv; + UserObj_t *uoptr = 0; + void *padb; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Open Database", + "An error occurred while trying to add " + "the password to the database."); + } + /* + * If NULL pwd, remove the user if it exists. + */ + else if (pwd == NULL) { + rv = nsadbRemoveUser(NULL, padb, DBPW_USER); + nsadbClose(padb, 0); + + /* + * If we get success(0) or a no such user error(NSAERRNAME) + * we're happy. + */ + if (rv != 0 && rv != NSAERRNAME) { + report_error(SYSTEM_ERROR, "Remove Password Failed", + "An error occurred while trying to remove " + "the password for the database."); + } + } else { + /* See if the user already exists, if so, just update it. */ + rv = nsadbFindByName(NULL, padb, DBPW_USER, AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + /* User doesn't exist, so add it. */ + uoptr = userCreate((NTS_t)DBPW_USER, (NTS_t)pw_enc(pwd), (NTS_t)DBPW_USER); + if (uoptr == 0) { + report_error(SYSTEM_ERROR, "Failed To Update Database", + "An error occurred while trying to add " + "the password to the database."); + rv = 1; + } + else { + rv = nsadbCreateUser(NULL, padb, uoptr); + } + } else { + uoptr->uo_pwd = (NTS_t)STRDUP(pw_enc(pwd)); + rv = nsadbModifyUser(NULL, padb, uoptr); + } + + nsadbClose(padb, 0); + + if (uoptr) { + if (rv < 0) { + report_error(SYSTEM_ERROR, "Failed To Set Database Password", + "An error occurred while trying to save " + "the password in the database."); + rv = 1; + } + userFree(uoptr); + } + } + return rv; +} + +/* + * checkdbpw - Return TRUE if the password is correct, or database + * doesn't have one, because the password user isn't there. + * Return FALSE if required password is not correct. + */ +NSAPI_PUBLIC int checkdbpw(char *db_path, char *pwd) +{ + int rv; + UserObj_t *uoptr = 0; + void *padb; + int fpwOK = 0; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv == 0) { + rv = nsadbFindByName(NULL, padb, DBPW_USER, AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + fpwOK = 1; /* Password userid isn't there, so none required. */ + } else { + if (pwd == NULL) + fpwOK = 0; /* PW user exists, no pw passed in, return false. */ + else { + if (pw_cmp(pwd, (char *)uoptr->uo_pwd)) + fpwOK = 0; /* passwords are different, so return false. */ + else + fpwOK = 1; /* passwords are the same, so return true. */ + } + userFree(uoptr); + } + nsadbClose(padb, 0); + } + return fpwOK; +} + +/* + * Create a link to another CGI: + * val - value text on the link + * targ - name of the CGI to start + * arg - argument to pass to the CGI + */ +void output_cgi_link(char *val, char *trg, char *arg) +{ + char line[BIG_LINE]; + sprintf(line, "%s?%s", trg, arg); + printf("<a href=index?options+acss+%s target='options'>%s</a>", + util_uri_escape(NULL, line), val); +} + +/* + * groupEnumCB - callback function from libaccess group enumerator + */ +static int groupEnumCB (NSErr_t * errp, + void * padb, void *parg, GroupObj_t *goptr) +{ + if (goptr && goptr->go_name && strlen((char *)goptr->go_name)) + ulsAddToList(parg, goptr->go_gid, (char *)goptr->go_name); + + return 0; /* 0: continue enumeration */ +} + +/* + * userEnumCB - callback function from libaccess group enumerator + */ +static int userEnumCB (NSErr_t * errp, + void * padb, void *parg, UserObj_t *uoptr) +{ + if (uoptr && uoptr->uo_name && strlen((char *)uoptr->uo_name)) + ulsAddToList(parg, uoptr->uo_uid, (char *)uoptr->uo_name); + + return 0; /* 0: continue enumeration */ +} + +/* + * idfound - horribly inefficient scan through the idlist table + * returning true if the specified id is found, false + * otherwise. + */ +int idfound(int id, int *idlist, int count) +{ + int i; + for (i = 0; i < count; ++i) { + if (id == idlist[i]) + return 1; + } + return 0; +} + +void output_groups_user_is_in(char *db_path, char *user) +{ + int rv; + UserObj_t *uoptr = 0; + GroupObj_t *goptr = 0; + void *padb; + USI_t *gidlist; + int i; + int id; + char *group; + char *gname; + int groupCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list group membership."); + } else { + /* See if the user exists. */ + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + /* User doesn't exist, so we've failed. */ + report_error(SYSTEM_ERROR, user, + "Unable to find user when trying to " + "list group membership."); + rv = 1; + } else { + groupCount = UILCOUNT(&uoptr->uo_groups); + if (groupCount > 0) { + void *DirectlyInList; + void *IndirectlyInList; + ulsAlloc(&DirectlyInList); + ulsAlloc(&IndirectlyInList); + + gidlist = UILLIST(&uoptr->uo_groups); + for (i = 0; i < groupCount; ++i) { + rv = nsadbIdToName(NULL, + padb, gidlist[i], AIF_GROUP, &gname); + if (rv >= 0) { + rv = nsadbFindByName(NULL, padb, gname, AIF_GROUP, + (void **)&goptr); + } + if (goptr != 0) { + if (goptr->go_name && strlen((char *)goptr->go_name)) { + if (idfound(uoptr->uo_uid, + (int*)UILLIST(&goptr->go_users), + UILCOUNT(&goptr->go_users))) { + ulsAddToList(DirectlyInList, goptr->go_gid, + (char *)goptr->go_name); + } + else { + ulsAddToList(IndirectlyInList, goptr->go_gid, + (char *)goptr->go_name); + } + } + groupFree(goptr); + goptr = 0; + } + } + ulsSortName(DirectlyInList); + ulsGetCount(DirectlyInList, &groupCount); + for (i=0; i<groupCount; ++i) { + group = NULL; + ulsGetEntry(DirectlyInList, i, &id, &group); + if (group) { + printf("<tr><td>"); + printf("Member of <b>%s</b></td><td>", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf("</td><td>"); + sprintf(line, "remfromgrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Remove from Group", "grped", line); + printf("</td>\n"); + } + } + ulsSortName(IndirectlyInList); + ulsGetCount(IndirectlyInList, &groupCount); + for (i=0; i<groupCount; ++i) { + group = NULL; + ulsGetEntry(IndirectlyInList, i, &id, &group); + if (group) { + printf("<tr><td>"); + printf("Indirect member of <b>%s</b></td><td>", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", group); + printf("</td><td>"); + sprintf(line, "addtogrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Add to Group", "grped", line); + printf("</td>\n"); + } + } + ulsFree(&DirectlyInList); + ulsFree(&IndirectlyInList); + } + } + } + return; +} + +/* + * output a table with the groups the user isn't a member of + */ +void output_nonmembership(char *db_path, char *user) +{ + int rv; + UserObj_t *uoptr = 0; + void *padb; + USI_t *gidlist; + int i; + int id; + char *group; + int groupCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list group membership."); + } else { + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + report_error(SYSTEM_ERROR, user, + "Unable to find user when trying to " + "list group membership."); + rv = 1; + } else { + void *sortList; + + ulsAlloc(&sortList); + rv = nsadbEnumerateGroups(NULL, padb, + (void *)sortList, groupEnumCB); + + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + + if (groupCount > 0) { + gidlist = UILLIST(&uoptr->uo_groups); + for (i=0; i<groupCount; ++i) { + group = NULL; + ulsGetEntry(sortList, i, &id, &group); + if (group && !idfound(id, (int*)gidlist, UILCOUNT(&uoptr->uo_groups))){ + printf("<tr><td>"); + printf("Not a member of <b>%s</b></td><td>", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf("</td><td>"); + sprintf(line, "addtogrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Add to Group", "grped", line); + printf("</td>\n"); + } + } + } + ulsFree(&sortList); + userFree(uoptr); + } + } + return; +} + +/* + * output_group_membership - output a table showing which + * groups a user is in. + */ +void output_group_membership(char *db_path, char *user) +{ + printf("<table border=1><caption align=left>\n"); + printf("<b>%s group membership:</b>", user); + printf("</caption>\n"); + output_groups_user_is_in(db_path, user); + output_nonmembership(db_path, user); + printf("</table>\n"); +} + +void output_grpgroup_membership(char *db_path, char *group, char *filter) +{ + int rv; + GroupObj_t *goptr = 0; + void *padb; + USI_t *gidlist; + int i; + int id; + char *gname; + char *memgroup; + int groupCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list group membership."); + } else { + /* See if the group exists. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + /* Group doesn't exist, so we've failed. */ + report_error(SYSTEM_ERROR, group, + "Unable to find group when trying to " + "list group membership."); + rv = 1; + } else { + groupCount = UILCOUNT(&goptr->go_groups); + if (groupCount > 0) { + void *sortList; + ulsAlloc(&sortList); + + printf("<table border=1><caption align=left>\n"); + printf("<b>%s has these group members:</b>", group); + printf("</caption>\n"); + gidlist = UILLIST(&goptr->go_groups); + for (i = 0; i < groupCount; ++i) { + rv = nsadbIdToName(NULL, + padb, gidlist[i], AIF_GROUP, &gname); + if ((rv >= 0) && (gname != 0) && (strlen(gname) != 0)) { + ulsAddToList(sortList, gidlist[i], gname); + } + } + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + for (i=0; i<groupCount; ++i) { + memgroup = NULL; + ulsGetEntry(sortList, i, &id, &memgroup); + if (memgroup && passfilter(memgroup, filter)) { + printf("<tr><td>"); + printf("<b>%s</b></td><td>", memgroup); + sprintf(line, "group=%s", memgroup); + output_cgi_link("Edit Group", "grped", line); + printf("</td><td>"); + sprintf(line, "remfromgrp_but=1&memgroup=%s&group=%s", + memgroup, group); + output_cgi_link("Remove from Group", "grped", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + ulsFree(&sortList); + } else { + printf("<b>This group has no group members.</b>"); + } + groupFree(goptr); + } + nsadbClose(padb, 0); + } + return; +} + +/* + * Output a table showing the user members of a group. + */ +NSAPI_PUBLIC void output_user_membership(char *db_path, char *group, char *filter) +{ + int rv; + GroupObj_t *goptr = 0; + void *padb; + USI_t *uidlist; + char *user; + int i; + int id; + char *memuser; + int userCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list user membership."); + } else { + /* See if the group exists. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + /* Group doesn't exist, so we've failed. */ + nsadbClose(padb, 0); + report_error(SYSTEM_ERROR, group, + "Unable to find group when trying to " + "list user membership."); + rv = 1; + } else { + userCount = UILCOUNT(&goptr->go_users); + if (userCount > 0) { + void *sortList; + ulsAlloc(&sortList); + + printf("<table border=1><caption align=left>\n"); + printf("<b>%s has these user members:</b>", group); + printf("</caption>\n"); + uidlist = UILLIST(&goptr->go_users); + for (i = 0; i < userCount; ++i) { + rv = nsadbIdToName(NULL, + padb, uidlist[i], AIF_USER, &user); + if ((rv >= 0) && (user != 0) && (strlen(user) != 0)) { + ulsAddToList(sortList, uidlist[i], user); + } + } + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &userCount); + for (i=0; i<userCount; ++i) { + memuser = NULL; + ulsGetEntry(sortList, i, &id, &memuser); + if (memuser && passfilter(memuser, filter)) { + printf("<tr><td>"); + printf("<b>%s</b></td><td>", memuser); + sprintf(line, "user=%s", memuser); + output_cgi_link("Edit User", "usred", line); + printf("</td><td>"); + sprintf(line, "remfromgrp_but=1&memuser=%s&group=%s", + memuser, group); + output_cgi_link("Remove from Group", "grped", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + ulsFree(&sortList); + } else { + nsadbClose(padb, 0); + printf("<b>This group has no user members.</b>"); + } + } + } + return; +} + +/* + * Output a group showing all users. + */ +NSAPI_PUBLIC int output_users_list(char *db_path, char *filter) +{ + int rv; + void *padb; + int i; + int id; + char *user; + int userCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list users."); + else { + void *sortList; + + ulsAlloc(&sortList); + rv = nsadbEnumerateUsers(NULL, padb, (void *)sortList, userEnumCB); + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &userCount); + + if (userCount > 0) { + + printf("<table border=1><caption align=left>\n"); + printf("<b>User List:</b>"); + printf("</caption>\n"); + + for (i=0; i<userCount; ++i) { + user = NULL; + ulsGetEntry(sortList, i, &id, &user); + if (user && passfilter(user, filter)) { + printf("<tr><td>"); + printf("<b>%s</b></td><td>", user); + sprintf(line, "user=%s", user); + output_cgi_link("Edit User", "usred", line); + printf("</td><td>\n"); + output_cgi_link("Remove User", "usrrem", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + } else { + printf("<b>There are no users in the database.</b>"); + } + ulsFree(&sortList); + } + return rv; +} + +/* + * Output a table showing all groups. + */ +NSAPI_PUBLIC int output_groups_list(char *db_path, char *filter) +{ + int rv; + void *padb; + int i; + int id; + char *group; + int groupCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list groups."); + else { + void *sortList; + + ulsAlloc(&sortList); + rv = nsadbEnumerateGroups(NULL, padb, (void *)sortList, groupEnumCB); + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + + if (groupCount > 0) { + + printf("<table border=1><caption align=left>\n"); + printf("<b>Group List:</b>"); + printf("</caption>\n"); + + for (i=0; i<groupCount; ++i) { + group = NULL; + ulsGetEntry(sortList, i, &id, &group); + if ((group) && (passfilter(group, filter))) { + printf("<tr><td>"); + printf("<b>%s</b></td><td>", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf("</td><td>"); + output_cgi_link("Remove Group", "grprem", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + } else { + printf("<b>There are no groups in the database.</b>"); + } + ulsFree(&sortList); + } + return rv; +} + +/* Helper function: Return a uls list of all the groups a user is in. MLM */ +void *_list_user_groups(void *padb, char *user, int group_users) +{ + int rv; + register int i; + UserObj_t *uoptr = 0; + GroupObj_t *ugoptr = 0; + GroupObj_t *goptr = 0; + void *userGroupList = NULL; + int userGroupCount= 0; + USI_t *gidlist; + char *ugname = NULL; + + if(!group_users) { + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + } else { + rv = nsadbFindByName(NULL, padb, user, AIF_GROUP, (void **)&ugoptr); + } + if ((uoptr == 0) && (ugoptr == 0)) { + /* User doesn't exist, so we've failed. */ + return NULL; + } else { + if(uoptr) { + userGroupCount = UILCOUNT(&uoptr->uo_groups); + } else { + userGroupCount = UILCOUNT(&ugoptr->go_groups); + } + if (userGroupCount > 0) { + ulsAlloc(&userGroupList); + if(uoptr) { + gidlist = UILLIST(&uoptr->uo_groups); + } else { + gidlist = UILLIST(&ugoptr->go_groups); + } + + for (i = 0; i < userGroupCount; ++i) { + rv = nsadbIdToName(NULL, padb, + gidlist[i], AIF_GROUP, &ugname); + + if (rv >= 0) { + rv = nsadbFindByName(NULL, padb, ugname, AIF_GROUP, + (void **)&goptr); + } + if (goptr != 0) { + if (goptr->go_name && strlen((char *)goptr->go_name)) { + if(uoptr) { + if (idfound(uoptr->uo_uid, + (int*)UILLIST(&goptr->go_users), + UILCOUNT(&goptr->go_users))) { + ulsAddToList(userGroupList, goptr->go_gid, + (char *)goptr->go_name); + } + } else { + ulsAddToList(userGroupList, goptr->go_gid, + (char *)goptr->go_name); + } + } + groupFree(goptr); + goptr = 0; + } + } + } + } + return userGroupList; +} + +/* Output a selector box, with name "name", and make it a multiple + * selector box if multiple=1. */ +/* If user is non-null, then if it's a multiple selector, correctly highlight + * the groups the user is in. + * If group_user is 1, then the variable "user" refers to a *group* as + * a member, rather than a user. + * Highlight the item "highlight" regardless of membership (as long as + * it's non-NULL.) MLM */ +NSAPI_PUBLIC void output_group_selector(char *db_path, int group_user, + char *user, + char *highlight, char *except, + char *name, int none, int multiple) +{ + int rv; + void *padb; + int i, j, isselected; + int id; + char *group; + int groupCount; + void *userGroupList = NULL; + int userGroupCount= 0; + char *ugname = NULL; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list groups."); + else { + void *sortList; + + ulsAlloc(&sortList); + rv = nsadbEnumerateGroups(NULL, padb, (void *)sortList, groupEnumCB); + + if((multiple) && (user)) { + userGroupList=_list_user_groups(padb, user, group_user); + if(userGroupList) { + ulsSortName(userGroupList); + ulsGetCount(userGroupList, &userGroupCount); + } + } + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + + if (groupCount > 0) { + /* Make a pulldown if we can. If the size is bigger than the + * overflow value, make it a box to hack around the fact that + * the X Navigator can't scroll pulldown lists. */ + if((multiple) || (groupCount > SELECT_OVERFLOW)) { + printf("<SELECT size=5 name=%s %s>", + name, multiple? "MULTIPLE" : ""); + } else { + printf("<SELECT name=%s>", name); + } + if((!multiple) && (none)) { + printf("<OPTION value=NONE>NONE\n"); + } + + for (i=0; i<groupCount; ++i) { + group = NULL; + ugname = NULL; + isselected=0; + ulsGetEntry(sortList, i, &id, &group); + if (group) { + if((except) && (!strcmp(group, except))) + continue; + if((highlight) && (!strcmp(group, highlight))) + isselected=1; + if(userGroupList && !isselected) { + for(j=0; j < userGroupCount; j++) { + ulsGetEntry(userGroupList, j, &id, &ugname); +#if 0 + if(ugname[0] > group[0]) { + /* Both lists are sorted, therefore, if we've + * hit a letter that's after the group letter, + * it must not be here. */ + /* What can I say, it's a pathetic attempt + * on my part to mask the fact that I know + * this is inefficient. */ + break; + } +#endif + if(!strcmp(ugname, group)) { + isselected=1; + break; + } + } + } + printf("<OPTION %s>%s\n", + isselected? "SELECTED" : "", group); + } + } + printf("</SELECT>"); + } else { + printf("<b>(No groups have been created.)</b>"); + } + ulsFree(&sortList); + if(userGroupList) + ulsFree(&userGroupList); + } +} + +void *_list_group_users(void *padb, char *group, int group_users) +{ + int rv; + GroupObj_t *goptr = 0; + USI_t *uidlist; + char *user; + int i; + int userCount; + void *sortList=NULL; + + /* See if the group exists. */ + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + /* Group doesn't exist, so we've failed. */ + return NULL; + } else { + if(group_users) + userCount = UILCOUNT(&goptr->go_groups); + else + userCount = UILCOUNT(&goptr->go_users); + + if (userCount > 0) { + ulsAlloc(&sortList); + + if(group_users) + uidlist = UILLIST(&goptr->go_groups); + else + uidlist = UILLIST(&goptr->go_users); + + for (i = 0; i < userCount; ++i) { + if(group_users) + rv = nsadbIdToName(NULL,padb, uidlist[i], AIF_GROUP, &user); + else + rv = nsadbIdToName(NULL, padb, uidlist[i], AIF_USER, &user); + if ((rv >= 0) && (user != 0) && (strlen(user) != 0)) { + ulsAddToList(sortList, uidlist[i], user); + } + } + } + } + return sortList; +} + +/* + * Output a selector box of users. If group is non-null, highlight the + * users in that group. MLM + */ +NSAPI_PUBLIC void output_user_selector(char *db_path, char *group, + char *highlight, char *except, + char *name, int none, int multiple) +{ + int rv; + void *padb; + int i, j, isselected; + int id; + char *user; + int userCount; + void *groupUserList = NULL; + int groupUserCount= 0; + char *guname = NULL; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list groups."); + else { + void *sortList; + + ulsAlloc(&sortList); + rv = nsadbEnumerateUsers(NULL, padb, (void *)sortList, userEnumCB); + + if((multiple) && (group)) { + groupUserList=_list_group_users(padb, group, 0); + if(groupUserList) { + ulsSortName(groupUserList); + ulsGetCount(groupUserList, &groupUserCount); + } + } + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &userCount); + + if (userCount > 0) { + /* Make a pulldown if we can. If the size is bigger than the + * overflow value, make it a box to hack around the fact that + * the X Navigator can't scroll pulldown lists. */ + if((multiple) || (userCount > SELECT_OVERFLOW)) { + printf("<SELECT size=5 name=%s %s>", + name, multiple? "MULTIPLE" : ""); + } else { + printf("<SELECT name=%s>", name); + } + if((!multiple) && (none)) { + printf("<OPTION value=NONE>NONE\n"); + } + + for (i=0; i<userCount; ++i) { + user = NULL; + guname = NULL; + isselected=0; + ulsGetEntry(sortList, i, &id, &user); + if (user) { + if((except) && (!strcmp(user, except))) + continue; + if((highlight) && (!strcmp(user, highlight))) + isselected=1; + if(groupUserList && !isselected) { + for(j=0; j < groupUserCount; j++) { + ulsGetEntry(groupUserList, j, &id, &guname); +#if 0 + if(guname[0] > user[0]) { + /* Both lists are sorted, therefore, if we've + * hit a letter that's after the group letter, + * it must not be here. */ + /* What can I say, it's a pathetic attempt + * on my part to mask the fact that I know + * this is inefficient. */ + break; + } +#endif + if(!strcmp(guname, user)) { + isselected=1; + break; + } + } + } + printf("<OPTION %s>%s\n", + isselected? "SELECTED" : "", user); + } + } + printf("</SELECT>"); + } else { + printf("<b>(No users have been created.)</b>"); + } + ulsFree(&sortList); + if(groupUserList) + ulsFree(&groupUserList); + } +} + + +int _item_in_list(char *item, char **list) +{ + register int i; + if(!list) return -1; + for(i=0; list[i]; i++) { + if(!strcmp(list[i], item)) + return i; + } + return -1; +} + +/* Take a char ** null terminated list of group names, and change a user's + * memberships so those are the only groups he's in. MLM */ +NSAPI_PUBLIC void change_user_membership(char *db_path, char *user, + char **new_groups) +{ + void *sortList; + int rv; + void *padb; + UserObj_t *uoptr = NULL; + GroupObj_t *goptr = NULL; + int groupCount=0; + register int i; + int index; + int id; + char *group; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list groups."); + + sortList=_list_user_groups(padb, user, 0); + if(sortList) { + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + } + + rv = nsadbFindByName(NULL, padb, user, AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + report_error(INCORRECT_USAGE, user, "The user was not found."); + } + + /* First check the groups he's already in. Remove any that no longer + * appear in the list. */ + for(i=0; i<groupCount; ++i) { + ulsGetEntry(sortList, i, &id, &group); + + if( (index=_item_in_list(group, new_groups)) == -1) { + goptr=0; + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + report_error(INCORRECT_USAGE, group, + "The group was not found."); + } + rv = nsadbRemUserFromGroup(NULL, padb, goptr, uoptr); + } else { + /* This group is in the list, so mark it as taken care of. */ + if(new_groups) + new_groups[index][0]='\0'; + } + } + /* Add the user to any remaining groups. */ + if(new_groups) { + for(i=0; new_groups[i]; i++) { + if(new_groups[i][0] == '\0') + continue; + goptr=0; + rv = nsadbFindByName(NULL, padb, new_groups[i], + AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + report_error(INCORRECT_USAGE, group,"The group was not found."); + } + rv = nsadbAddUserToGroup(NULL, padb, goptr, uoptr); + } + } + + nsadbClose(padb, 0); +} + +/* Take a char ** null terminated list of user names, and change a group's + * memberships so those are the only users it has. MLM */ +/* Again, if group_users is 1, then the new_users are assumed to be groups. */ +NSAPI_PUBLIC void change_group_membership(char *db_path, char *group, + int group_users, char **new_users) +{ + void *sortList; + int rv; + void *padb; + UserObj_t *uoptr = NULL; + GroupObj_t *goptr = NULL; + GroupObj_t *ugoptr = NULL; + int userCount=0; + register int i; + int index; + int id; + char *user; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list groups."); + + sortList=_list_group_users(padb, group, group_users); + if(sortList) { + ulsSortName(sortList); + ulsGetCount(sortList, &userCount); + } + + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + report_error(INCORRECT_USAGE, group, "The group was not found."); + } + + /* First check the users already there. Remove any that no longer + * appear in the list. */ + for(i=0; i<userCount; ++i) { + ulsGetEntry(sortList, i, &id, &user); + if( (index=_item_in_list(user, new_users)) == -1) { + if(group_users) { + ugoptr=0; + rv = nsadbFindByName(NULL, padb, user, AIF_GROUP, + (void **)&ugoptr); + if (ugoptr == 0) { + report_error(INCORRECT_USAGE, user, + "The group was not found."); + } + rv = nsadbRemGroupFromGroup(NULL, padb, goptr, ugoptr); + } else { + uoptr=0; + rv = nsadbFindByName(NULL, padb, user, AIF_USER, + (void **)&uoptr); + if (uoptr == 0) { + report_error(INCORRECT_USAGE, user, + "The user was not found."); + } + rv = nsadbRemUserFromGroup(NULL, padb, goptr, uoptr); + } + } else { + /* This user is in the list, so mark it as taken care of. */ + if(new_users) + new_users[index][0]='\0'; + } + } + /* Add any remaining users. */ + if(new_users) { + for(i=0; new_users[i]; i++) { + if(new_users[i][0] == '\0') + continue; + if(group_users) { + ugoptr=0; + rv = nsadbFindByName(NULL, padb, new_users[i], + AIF_GROUP, (void **)&ugoptr); + if (ugoptr == 0) { + report_error(INCORRECT_USAGE, new_users[i], + "The group was not found."); + } + rv = nsadbAddGroupToGroup(NULL, padb, goptr, ugoptr); + if(rv) report_error(SYSTEM_ERROR, new_users[i], + "Unable to add group to group"); + } else { + uoptr=0; + rv = nsadbFindByName(NULL, padb, new_users[i], + AIF_USER, (void **)&uoptr); + if (uoptr == 0) { + report_error(INCORRECT_USAGE, new_users[i], + "The user was not found."); + } + rv = nsadbAddUserToGroup(NULL, padb, goptr, uoptr); + if(rv) report_error(SYSTEM_ERROR, new_users[i], + "Unable to add user to group"); + } + } + } + + nsadbClose(padb, 0); +} + +/* + * output a table with the groups that aren't members of the group + */ +NSAPI_PUBLIC void output_nongrpgroup_membership(char *db_path, char *group, char *filter) +{ + int rv; + GroupObj_t *goptr = 0; + void *padb; + USI_t *gidlist; + int i; + int id; + char *memgroup; + int groupCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list group membership."); + } else { + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + report_error(SYSTEM_ERROR, group, + "Unable to find group when trying to " + "list group membership."); + rv = 1; + nsadbClose(padb, 0); + } else { + void *sortList; + + printf("<table border=1><caption align=left>\n"); + printf("<b>These groups are not members of %s:</b>", group); + printf("</caption>\n"); + + ulsAlloc(&sortList); + rv = nsadbEnumerateGroups(NULL, + padb, (void *)sortList, groupEnumCB); + nsadbClose(padb, 0); + ulsSortName(sortList); + ulsGetCount(sortList, &groupCount); + + if (groupCount > 0) { + gidlist = UILLIST(&goptr->go_groups); + for (i=0; i<groupCount; ++i) { + memgroup = NULL; + ulsGetEntry(sortList, i, &id, &memgroup); + if ( memgroup && + strcmp(memgroup, group) && + !idfound(id, (int*)gidlist, UILCOUNT(&goptr->go_groups)) && + passfilter(memgroup, filter)) { + printf("<tr>"); + printf("<td><b>%s</b></td><td>", memgroup); + sprintf(line, "group=%s", memgroup); + output_cgi_link("Edit Group", "grped", line); + printf("</td><td>"); + sprintf(line, "addtogrp_but=1&memgroup=%s&group=%s", + memgroup, group); + output_cgi_link("Add to Group", "grped", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + } + ulsFree(&sortList); + groupFree(goptr); + } + } + return; +} + +/* + * output a table with the users that aren't members of the group + */ +NSAPI_PUBLIC void output_nonuser_membership(char *db_path, char *group, char *filter) +{ + int rv; + GroupObj_t *goptr = 0; + void *padb; + USI_t *gidlist; + int i; + int id; + char *memuser; + int userCount; + char line[BIG_LINE]; + + rv = nsadbOpen(NULL, db_path, 0, &padb); + if (rv < 0) { + report_error(SYSTEM_ERROR, db_path, + "Failed to open database while trying " + "to list group membership."); + } else { + rv = nsadbFindByName(NULL, padb, group, AIF_GROUP, (void **)&goptr); + if (goptr == 0) { + nsadbClose(padb, 0); + report_error(SYSTEM_ERROR, group, + "Unable to find group when trying to " + "list user membership."); + rv = 1; + } else { + void *sortList; + + printf("<table border=1><caption align=left>\n"); + printf("<b>These users are not members of %s:</b>", group); + printf("</caption>\n"); + + ulsAlloc(&sortList); + rv = nsadbEnumerateUsers(NULL, padb, (void *)sortList, userEnumCB); + nsadbClose(padb, 0); + + ulsSortName(sortList); + ulsGetCount(sortList, &userCount); + + if (userCount > 0) { + gidlist = UILLIST(&goptr->go_users); + for (i=0; i<userCount; ++i) { + memuser = NULL; + ulsGetEntry(sortList, i, &id, &memuser); + if ( memuser && + !idfound(id, (int*)gidlist, UILCOUNT(&goptr->go_users)) && + passfilter(memuser, filter)) { + printf("<tr>"); + printf("<td><b>%s</b></td><td>", memuser); + sprintf(line, "memuser=%s", memuser); + output_cgi_link("Edit User", "usred", line); + printf("</td><td>"); + sprintf(line, "addtogrp_but=1&memuser=%s&group=%s", + memuser, group); + output_cgi_link("Add to Group", "grped", line); + printf("</td>\n"); + } + } + printf("</table>\n"); + } + ulsFree(&sortList); + groupFree(goptr); + } + } + return; +} + +NSAPI_PUBLIC char *get_acl_file() +{ + char line[BIG_LINE]; + char *acl_file = get_mag_var("ACLFile"); + if (!acl_file) { + sprintf(line, "%s%cgenerated.%s.acl", + get_httpacl_dir(), FILE_PATHSEP, get_srvname(0)); + set_mag_var("ACLFile", line); + acl_file = STRDUP(line); + } + if(!file_exists(acl_file)) { + FILE *f; + if(! (f=fopen(acl_file, "w")) ) + report_error(FILE_ERROR, acl_file, "Could not open file."); + fclose(f); + } + return acl_file; +} + +NSAPI_PUBLIC char *get_workacl_file() +{ + char line[BIG_LINE]; + char *workacl_file; + sprintf(line, "%s%cgenwork.%s.acl", + get_httpacl_dir(), FILE_PATHSEP, get_srvname(0)); + workacl_file = STRDUP(line); + + if(!file_exists(workacl_file)) { + FILE *f; + char *current=get_acl_file(); + if(file_exists(current)) { + cp_file(current, workacl_file, 0644); + } else { + if(! (f=fopen(workacl_file, "w")) ) + report_error(FILE_ERROR, workacl_file, "Could not open file."); + fclose(f); + } + } + return workacl_file; +} + +/* + * get_acl_info - Open the specified ACL file. Return a context + * handle into it, the data it contains, and whether this is + * a default allow or default deny resource. + * + * Returns: 0 if it works + * AUTHDB_ACL_FAIL (-1) if it fails + * AUTHDB_ACL_ODD_ACL (-2) if the ACL doesn't appear to be one + * generated by the CGI. + */ +NSAPI_PUBLIC int get_acl_info(char *acl_file, char *acl_name, void **pacl, + char ***hosts, authInfo_t **authinfo, + char ***users, char ***userhosts, + int *fdefaultallow) +{ + int rv = 0; + char *acl_sig = NULL; + ACContext_t *acl_con = NULL; + ACL_t *acl; + + if (hosts) + *hosts = NULL; + if (authinfo) + *authinfo = NULL; + if (users) + *users = NULL; + if (userhosts) + *userhosts = NULL; + if (fdefaultallow) + *fdefaultallow = 0; + + if ((rv = accReadFile(NULL, acl_file, &acl_con))) + rv = AUTHDB_ACL_FAIL; + else if (!(rv = aclFindByName(acl_con, acl_name, NULL, 0, &acl))) + rv = AUTHDB_ACL_NOT_FOUND; + else { + /* + * If we get the ACL open, get it's signiture to see if + * it looks like one of ours. Each directive is identified + * by a single character in the signiture string. The ones + * we care about are: + * 'a' - default allow + * 'd' - default deny + * 'r' - default authenticate + * + * The ACL used for default allow access has these directives: + * default allow anyone at *; (required, shows up as an 'a') + * default deny anyone at (hosts); (optional, shows up as a 'd') + * + * The ACL used for default deny access has these directives: + * default deny anyone at *; (required, shows up as an 'd') + * default allow anyone at (hosts); (optional, shows up as a 'a') + * default authenticate... (optional, shows up as an 'r') + * default allow (users) at (hosts); (optional, shows up as a 'a') + * + * Valid signitures are: + * "a" + * "ad" + * "d" + * "da" + * "dra" + * "dara" + */ + + if (acl) + acl_sig = aclGetSignature(acl); + + if (acl_sig) { + if (!strcmp(acl_sig, "a")) { + if (fdefaultallow) + *fdefaultallow = 1; + rv = 0; + } + else if (!strcmp(acl_sig, "ad")) { + if (fdefaultallow) + *fdefaultallow = 1; + if (hosts) + *hosts = aclGetHosts(acl, 2, 1); + rv = 0; + } + else if (!strcmp(acl_sig, "d")) { + if (fdefaultallow) + *fdefaultallow = 0; + rv = 0; + } + else if (!strcmp(acl_sig, "da")) { + if (fdefaultallow) + *fdefaultallow = 0; + if (hosts) + *hosts = aclGetHosts(acl, 2, 1); + rv = 0; + } + else if (!strcmp(acl_sig, "dra")) { + if (fdefaultallow) + *fdefaultallow = 0; + if (authinfo) { + char *p; + *authinfo = (authInfo_t *)MALLOC(sizeof(authInfo_t)); + memset(*authinfo, 0, (sizeof(authInfo_t))); + if ((p = aclGetAuthMethod(acl, 2))) + (*authinfo)->type = strdup(p); + if ((p = aclGetDatabase(acl, 2))) + (*authinfo)->db_path = strdup(p); + if ((p = aclGetPrompt(acl, 2))) + (*authinfo)->prompt = strdup(p); + } + if (users) + *users = aclGetUsers(acl, 3, 1); + if (userhosts) + *userhosts = aclGetHosts(acl, 3, 1); + rv = 0; + } + else if (!strcmp(acl_sig, "dara")) { + if (fdefaultallow) + *fdefaultallow = 0; + if (hosts) + *hosts = aclGetHosts(acl, 2, 1); + if (authinfo) { + char *p; + *authinfo = (authInfo_t *)MALLOC(sizeof(authInfo_t)); + memset(*authinfo, 0, (sizeof(authInfo_t))); + if ((p = aclGetAuthMethod(acl, 3))) + (*authinfo)->type = strdup(p); + if ((p = aclGetDatabase(acl, 3))) + (*authinfo)->db_path = strdup(p); + if ((p = aclGetPrompt(acl, 3))) + (*authinfo)->prompt = strdup(p); + } + if (users) + *users = aclGetUsers(acl, 4, 1); + if (userhosts) + *userhosts = aclGetHosts(acl, 4, 1); + rv = 0; + } + else + rv = AUTHDB_ACL_ODD_ACL; + } + if (pacl) + *pacl = (void *)acl_con; + } + return rv; +} + + +static void add_acl_rights(ACContext_t *acc) +{ + int rv; + char **p; + for (p = acl_read_rights; *p; ++p) { + rv = aclRightDef(NULL, acc, *p, NULL); + } + for (p = acl_write_rights; *p; ++p) { + rv = aclRightDef(NULL, acc, *p, NULL); + } +} + +/* + * delete_acl_by_name - remove a specified acl. + * + * Return: 0 if it deletes an ACL. Otherwise something else. + * + */ +NSAPI_PUBLIC int delete_acl_by_name(char *acl_file, char *acl_name) +{ + int rv = 1; + ACContext_t *acl_con = NULL; + ACL_t *acl = NULL; + + if (acl_file && acl_name) + { + rv = accReadFile(NULL, acl_file, &acl_con); + + if (!rv) { + rv = aclFindByName(acl_con, acl_name, NULL, 0, &acl); + if (rv == 1 && acl) { + aclDelete(acl); + rv = accWriteFile(acl_con, acl_file, 0); + set_commit(0, 1); + } + } + } + return rv; +} + +/* + * set_acl_info - Replaces the specified ACL with the information + * provided. The fdefaultallow is tells us whether + * to generate a default allow everyone type ACL, or + * a default deny everyone type ACL. + * + * If opening the ACL file fails with a file open + * type error, it is assumed to not exist, and a + * new one is created. + * + * Returns: 0 if it works + * AUTHDB_ACL_FAIL if it fails + */ +NSAPI_PUBLIC int set_acl_info(char *acl_file, char *acl_name, int prefix, + void **pacl, char **rights, + char **hosts, authInfo_t *authinfo, + char **users, char **userhosts, + int fdefaultallow) +{ + int rv = AUTHDB_ACL_FAIL; + ACContext_t *acl_con = NULL; + ACL_t *acl = NULL; + int amethod = AUTH_METHOD_BASIC; + char *db_path = NULL; + char *prompt = NULL; + + /* + * Digest parms + */ + if (authinfo) { + if (!strcmp(authinfo->type, "SSL")) + amethod = AUTH_METHOD_SSL; + else + amethod = AUTH_METHOD_BASIC; + db_path = authinfo->db_path; + prompt = authinfo->prompt; + } + + if (prefix) + prefix = ACLF_NPREFIX; + else + prefix = 0; + + /* + * Open the specified ACL file, destroy the existing ACL with + * the specified name if it exists, and then write the new + * stuff back out. + */ + if (acl_file && acl_name) + { + (void)delete_acl_by_name(acl_file, acl_name); + rv = accReadFile(NULL, acl_file, &acl_con); + + /* + * If the file isn't there, create an empty context. + */ + if (rv == ACLERROPEN) + rv = accCreate(0, 0, &acl_con); + + if (rv) + rv = AUTHDB_ACL_FAIL; + else { + (void)add_acl_rights(acl_con); + + if (aclMakeNew(acl_con, "", acl_name, + rights, prefix, &acl)) { + rv = AUTHDB_ACL_FAIL; + } + else if (aclPutAllowDeny( + NULL, acl, 0, fdefaultallow, NULL, NULL)) { + rv = AUTHDB_ACL_FAIL; + } + else if (hosts && + (rv = aclPutAllowDeny( + NULL, acl, 0, !fdefaultallow, NULL, hosts))) { + + rv = AUTHDB_ACL_FAIL; + } + else if (users && authinfo && + ((rv = aclPutAuth(NULL, acl, 0, amethod, db_path, prompt))|| + (rv = aclPutAllowDeny(NULL, acl, 0, !fdefaultallow, + users, userhosts)))) { + + rv = AUTHDB_ACL_FAIL; + } + else { + if (accWriteFile(acl_con, acl_file, 0)) { + rv = AUTHDB_ACL_FAIL; + } + else { + set_commit(0, 1); + rv = 0; + if (pacl) + *pacl = (void *)acl; + } + } + } + } + return rv; +} + +/* + * get_acl_names - Passes back the names of the read and write + * ACLs for the current resource. + * The directive parm is usually "acl", but is + * also used to find stashed items under names + * like "acl-disabled". + * Returns 0 if no other ACLs are found, 1 if an ACL that + * doesn't match the type of name we generate. + */ +NSAPI_PUBLIC int get_acl_names(char **readaclname, char **writeaclname, char *directive) +{ + pblock **pbs; + char *aclname; + int fother_acls = 0; + char **config = get_adm_config(); + char *curres = get_current_resource(config); + int rtype = get_current_restype(config); + int i; + + *readaclname = NULL; + *writeaclname = NULL; + + pbs = list_pblocks(rtype, curres, "PathCheck", CHECK_ACL_FN); + + if (pbs) { + for (i=0; pbs[i]; ++i) { + aclname = pblock_findval(directive, pbs[i]); + if (is_readacl(aclname)) + *readaclname = strdup(aclname); + else if (is_writeacl(aclname)) + *writeaclname = strdup(aclname); + else + fother_acls = 1; + } + } + return fother_acls; +} + +NSAPI_PUBLIC int is_readacl(char *name) { + if (name) + return strstr(name, ACLNAME_READ_COOKIE)?1:0; + else + return 0; +} + +NSAPI_PUBLIC int is_writeacl(char *name) { + if (name) + return strstr(name, ACLNAME_WRITE_COOKIE)?1:0; + else + return 0; +} + +NSAPI_PUBLIC int admin_is_ipaddr(char *p) +{ + int i; + int num_found = 0; + + if (!p || !p[0]) + return 0; /* NULL isn't an IP Address */ + else { + for (i=0; p[i]; ++i) { + if (isalpha(p[i])) + return 0; /* If it has an alpha character, it's not IP addr */ + else if (isdigit(p[i])) { + num_found = 1; + } + } + } + /* + * Well, hard to say what it is, but if there's at least a number + * in it, and the ip number checker can parse it, we'll call it + * an IP address; + */ + if (num_found && get_ip_and_mask(p)) + return 1; + else + return 0; +} + +/* + * Get the hostnames and ipaddrs strings from the single hosts array + * of string pointers. + */ +NSAPI_PUBLIC void get_hostnames_and_ipaddrs(char **hosts, char **hostnames, char **ipaddrs) +{ + char *p; + int nbipaddrs = 0; + int nbhostnames = 0; + int i; + if (hosts && hostnames && ipaddrs) { + *hostnames = NULL; + *ipaddrs = NULL; + /* + * Make two passes, once to total the size needed for + * the hosts and ipaddrs string, then alloc them and + * strcat the strings in. + */ + for(i=0, p=hosts[i]; p; p=hosts[++i]) { + if (admin_is_ipaddr(p)) + nbipaddrs += strlen(p) + 2; /* Teminator and "," */ + else + nbhostnames += strlen(p) + 2; + } + + if (nbhostnames) { + *hostnames = (char *)MALLOC(nbhostnames + 1); + memset(*hostnames, 0, nbhostnames); + } + if (nbipaddrs) { + *ipaddrs = (char *)MALLOC(nbipaddrs + 1); + memset(*ipaddrs, 0, nbipaddrs); + } + + /* + * We've got the space, now go look at each, strcat it + * into the correct string, prefixed with a "," for all + * but the first. + */ + for(i=0, p=hosts[i]; p; p=hosts[++i]) { + if (admin_is_ipaddr(p)) { + if (strlen(*ipaddrs)) + strcat(*ipaddrs, ","); + strcat(*ipaddrs, p); + } + else { + if (strlen(*hostnames)) + strcat(*hostnames, ","); + strcat(*hostnames, p); + } + } + } + return; +} + +/* + * Get the usernames and groups strings from the single users array + * of string pointers. + */ +NSAPI_PUBLIC void get_users_and_groups(char **users, char **usernames, char **groups, + char *db_path) +{ + char *p; + int nbusernames = 0; + int nbgroups = 0; + int i; + int is_user = 0; + int is_group = 0; + + if (users && usernames && groups) { + *usernames = NULL; + *groups = NULL; + /* + * Make two passes, once to total the size needed for + * the strings, then alloc them and strcat the strings in. + */ + for(i=0, p=users[i]; p; p=users[++i]) { + is_user = 0; + is_group = 0; + groupOrUser(db_path, p, &is_user, &is_group); + + /* Enclose user/group name in quotes if necessary */ + p = aclSafeIdent(p); + + if (is_user) + nbusernames += strlen(p) + 2; /* Teminator and "," */ + else if (is_group) + nbgroups += strlen(p) + 2; + } + + if (nbusernames) { + *usernames = (char *)MALLOC(nbusernames + 1); + memset(*usernames, 0, nbusernames); + } + if (nbgroups) { + *groups = (char *)MALLOC(nbgroups + 1); + memset(*groups, 0, nbgroups); + } + + /* + * We've got the space, now go look at each, strcat it + * into the correct string, prefixed with a "," for all + * but the first. + */ + for(i=0, p=users[i]; p; p=users[++i]) { + is_user = 0; + is_group = 0; + groupOrUser(db_path, p, &is_user, &is_group); + + /* Enclose user/group name in quotes if necessary */ + p = aclSafeIdent(p); + + if (is_user) { + if (strlen(*usernames)) + strcat(*usernames, ","); + strcat(*usernames, p); + } + else if (is_group) { + if (strlen(*groups)) + strcat(*groups, ","); + strcat(*groups, p); + } + } + } + return; +} + +/* + * Load from host and IP Addr strings into the array of + * pointers to strings. + */ +NSAPI_PUBLIC void load_host_array(char ***hosts, char *hostnames, char *ipaddrs) +{ + char *tok; + int nMax = 20; + int nCur = 0; + char **hostarr; + char *valid_ip; + + if (hosts) { + hostarr = new_strlist(nMax); + hostarr[0] = NULL; + if (hostnames) { + for (tok = strtok(hostnames, ","); + tok; + tok = strtok(NULL, ",")) + { + if (!(nCur < nMax)) { + nMax += 20; + hostarr = grow_strlist(hostarr, nMax); + } + hostarr[nCur] = strdup(tok); + hostarr[++nCur] = NULL; + } + } + if (ipaddrs) { + for (tok = strtok(ipaddrs, ","); + tok; + tok = strtok(NULL, ",")) + { + if (!(nCur < nMax)) { + nMax += 20; + hostarr = grow_strlist(hostarr, nMax); + } + valid_ip = get_ip_and_mask(tok); + if (valid_ip) { + hostarr[nCur] = strdup(valid_ip); + hostarr[++nCur] = NULL; + } + } + } + *hosts = hostarr; + } + + return; +} + +void load_users_array(char ***users, char *usernames, char *groups) +{ + char *tok; + int nMax = 20; + int nCur = 0; + char **userarr; + + if (users) { + userarr = new_strlist(nMax); + userarr[0] = NULL; + if (usernames) { + for (tok = strtok(usernames, ","); + tok; + tok = strtok(NULL, ",")) + { + if (!(nCur < nMax)) { + nMax += 20; + userarr = grow_strlist(userarr, nMax); + } + userarr[nCur] = strdup(tok); + userarr[++nCur] = NULL; + } + } + if (groups) { + for (tok = strtok(groups, ","); + tok; + tok = strtok(NULL, ",")) + { + if (!(nCur < nMax)) { + nMax += 20; + userarr = grow_strlist(userarr, nMax); + } + userarr[nCur] = strdup(tok); + userarr[++nCur] = NULL; + } + } + *users = userarr; + } + + return; +} + +/* Removes enclosing double quotes from a string (in place) */ +NSAPI_PUBLIC char * str_unquote(char * str) +{ + if (str) { + if (str[0] == '"') { + int len = strlen(str); + + if (str[len-1] == '"') { + str[len-1] = 0; + ++str; + } + } + } + + return str; +} diff --git a/lib/libadmin/error.c b/lib/libadmin/error.c new file mode 100644 index 00000000..a4f87842 --- /dev/null +++ b/lib/libadmin/error.c @@ -0,0 +1,109 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * error.c - Handle error recovery + * + * All blame to Mike McCool + */ + +#include "libadmin/libadmin.h" +#if 0 +#include "cgiutils/cgi-util.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef XP_WIN32 +#include <windows.h> +#include "base/nterr.h" +#endif + +#define ERROR_HTML "error.html" + +/* Be sure to edit libadmin.h and add new #define types for these headers. */ +char *error_headers[MAX_ERROR] = + {"File System Error", + "Memory Error", + "System Error", + "Incorrect Usage", + "Form Element Missing", + "Registry Database Error", + "Network Error", + "Unexpected Failure", + "Warning"}; + +#ifdef XP_UNIX +#define get_error() errno +#define verbose_error() system_errmsg() +#else /* XP_WIN32 */ +int get_error() +{ + int error = GetLastError(); + return(error ? error: WSAGetLastError()); +} +char *verbose_error() +{ + /* Initialize error hash tables */ + HashNtErrors(); + return alert_word_wrap(system_errmsg(), WORD_WRAP_WIDTH, "\\n"); +} +#endif /* XP_WIN32 */ + +void _report_error(int type, char *info, char *details, int shouldexit) +{ + /* Be sure headers are terminated. */ + fputs("\n", stdout); + + fprintf(stdout, "<SCRIPT LANGUAGE=\"%s\">", MOCHA_NAME); + output_alert(type, info, details, 0); + if(shouldexit) { + fprintf(stdout, "if(history.length>1) history.back();"); + } + fprintf(stdout, "</SCRIPT>\n"); + + if(shouldexit) { + WSACleanup(); + exit(0); + } +} + +/* + * Format and output a call to the JavaScript alert() function. + * The caller must ensure a JavaScript context. + */ +NSAPI_PUBLIC void output_alert(int type, char *info, char *details, int wait) +{ + char *wrapped=NULL; + int err; + + if(type >= MAX_ERROR) + type=DEFAULT_ERROR; + + wrapped=alert_word_wrap(details, WORD_WRAP_WIDTH, "\\n"); + + if(!info) info=""; + fprintf(stdout, (wait) ? "confirm(\"" : "alert(\""); + fprintf(stdout, "%s:%s\\n%s", error_headers[type], info, wrapped); + if(type==FILE_ERROR || type==SYSTEM_ERROR) { + err = get_error(); + if(err != 0) + fprintf(stdout, + "\\n\\nThe system returned error number %d, " + "which is %s.", err, verbose_error()); + } + fprintf(stdout, "\");"); +} + +NSAPI_PUBLIC void report_error(int type, char *info, char *details) +{ + _report_error(type, info, details, 1); +} + +NSAPI_PUBLIC void report_warning(int type, char *info, char *details) +{ + _report_error(type, info, details, 0); +} + diff --git a/lib/libadmin/strlist.c b/lib/libadmin/strlist.c new file mode 100644 index 00000000..46a0c600 --- /dev/null +++ b/lib/libadmin/strlist.c @@ -0,0 +1,46 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * strlist.c: Managing a handle to a list of strings + * + * All blame to Mike McCool + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "netsite.h" +#include <libadmin/libadmin.h> + +NSAPI_PUBLIC char **new_strlist(int size) +{ + char **new_list; + register int x; + + new_list = (char **) MALLOC((size+1)*(sizeof(char *))); +/* <= so we get the one right after the given size as well */ + for(x=0; x<= size; x++) + new_list[x] = NULL; + + return new_list; +} + +NSAPI_PUBLIC char **grow_strlist(char **strlist, int newsize) +{ + char **ans; + + ans = (char **) REALLOC(strlist, (newsize+1)*sizeof(char *)); + + return ans; +} + +NSAPI_PUBLIC void free_strlist(char **strlist) +{ + int x; + + for(x=0; (strlist[x]); x++) free(strlist[x]); + free(strlist); +} diff --git a/lib/libadmin/template.c b/lib/libadmin/template.c new file mode 100644 index 00000000..a689d3b5 --- /dev/null +++ b/lib/libadmin/template.c @@ -0,0 +1,820 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * template.c: The actual HTML templates in a static variable + * + * All blame to Mike McCool + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "template.h" +#include "libadmin/libadmin.h" +#include "libadmin/dbtlibadmin.h" +#include "base/util.h" + +/* If you add something to this structure, don't forget to document it + * in templates.h, and increase MAXTEMPLATE! + * + * Also, save yourself a lot of grief and put a space after the name. + */ + +static struct template_s templates[MAXTEMPLATE] = { + {"IF ", "FUNC conditional"}, + {"ELSE ", "FUNC conditional"}, + {"ENDIF ", "FUNC conditional"}, + {"TITLE ", "<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n" + "<BODY bgcolor=\"#C0C0C0\" link=\"#0000EE\" " + "vlink=\"#551A8B\" alink=\"#FF0000\" %s>\n"}, + {"PAGEHEADER ", "FUNC pageheader"}, + {"DOCSWITCHER ", ""}, + {"COPYRIGHT ", ""}, + {"RESOURCEPICKER ", "FUNC respicker"}, + {"BOOKTRACK ", "FUNC booktrack"}, + {"BEGININFO ", "<table border=2 width=100%% cellpadding=2>\n" + "<tr><td align=center colspan=2>" + "<b><FONT size=+1>%s</FONT></b></td></tr>" +#if 0 + "<tr><td>" + "<IMG src=\"../icons/b-open.gif\" hspace=8 alt=\"*\"" + "height=26 width=55></td>" + "<td>\n"}, +#endif + "<td colspan=2>\n"}, + {"ADDINFO ", "</td></tr><tr><td colspan=2>"}, + {"ENDINFO ", "</td></tr></table>\n<hr width=10%%>\n"}, + {"SUBMIT ", "FUNC submit\n"}, + {"DOCUMENTROOT ", "FUNC docroot"}, + {"BEGINELEM ", "<pre>"}, +/* {"ELEM ", "<hr width=100%%><b>%s</b>"}, */ + {"ELEM ", "\n<b>%s</b>"}, +/* {"ENDELEM ", "<hr width=100%%></pre>\n"}, */ + {"ENDELEM ", "</pre>\n"}, + {"ELEMADD ", "<b>%s</b>"}, +/* {"ELEMDIV ", "<hr width=100%%>"}, */ + {"ELEMDIV ", "\n"}, + {"REFERER ", "FUNC link_referer"}, + {"INDEX ", "<a href=\"index\">%s</a>\n"}, + {"SERVERROOT ", "FUNC serverroot"}, + {"RESTART ", "<a href=\"pcontrol\">%s</a>\n"}, + {"ACCESS ", "FUNC makeurl"}, + {"COMMIT ", "<a href=\"commit?commit\">%s</a>\n"}, + {"BACKOUT ", "<center>If you don't want to %s, you can <a href=index>" + "return to the server manager.</a></center>\n"}, + {"CURSERVNAME", "FUNC curservname"}, + {"VERIFY ", "FUNC verify"}, + {"HELPBUTTON", "FUNC helpbutton"}, + {"DIALOGSUBMIT", "FUNC dialogsubmit"}, + {"HELPJSFN", "FUNC helpjsfn"} +}; + +int get_directive(char *string); +void conditional(char *input, char **vars, int index); +void respicker(char **config); +void currentres(char **config); +void prevres(char **config); +void booktrack(char *input, char **vars); +void docswitcher(char *input); +void docroot(char **vars); +void link_referer(char **input, char **vars); +void serverroot(char **vars); +char **get_vars(char *string); +static void output(char *string); +void makeurl(char **vars); +void curservname(void); +void pageheader(char **vars, char **config); +void submit(int verify, char **vars); +void helpbutton(char *topic); +void dialogsubmit(char *topic); + +static int status = -1; + +/* Filter a page. Takes the page to filter as an argument. Uses above + * filters to process. + */ +NSAPI_PUBLIC int parse_line(char *line_input, char **input) +{ + register int index; + char *position; + int dirlen = strlen(DIRECTIVE_START); + char **vars; + + + if(!strncmp(line_input, DIRECTIVE_START, dirlen)) { + position = (char *) (line_input + dirlen); + index = get_directive(position); + + /* did we get one? */ + if(index != -1) { + /* if so, get the vars. */ + position += strlen(templates[index].name); + vars = get_vars(position); + /* Dispatch the correct function (done for readability, + * although I'm starting to wonder if I should bother) + */ + if(!strncmp(templates[index].format, "FUNC ", 5)) { + + if(!strncmp(templates[index].format+5, "conditional", 11)) + conditional(input[0], vars, index); + else if(!strncmp(templates[index].format+5, "respicker", 9)) + respicker(input); + else if(!strncmp(templates[index].format+5, "booktrack", 9)) + booktrack(input[0], vars); + else if(!strncmp(templates[index].format+5, "docswitcher", 11)) + docswitcher(input[0]); + else if(!strncmp(templates[index].format+5, "docroot", 7)) + docroot(vars); + else if(!strncmp(templates[index].format+5, "link_referer",12)) + link_referer(input, vars); + else if(!strncmp(templates[index].format+5, "serverroot",10)) + serverroot(vars); + else if(!strncmp(templates[index].format+5, "makeurl",7)) + makeurl(vars); + else if(!strncmp(templates[index].format+5, "curservname",11)) + curservname(); + else if(!strncmp(templates[index].format+5, "pageheader",10)) + pageheader(vars, input); + else if(!strncmp(templates[index].format+5, "submit",6)) + submit(0, vars); + else if(!strncmp(templates[index].format+5, "verify",6)) + submit(1, vars); + else if(!strncmp(templates[index].format+5, "helpbutton",10)) + helpbutton(vars[0]); + else if(!strncmp(templates[index].format+5, "dialogsubmit",12)) + dialogsubmit(vars[0]); + /* We don't know what this template is. Send it back. */ + else return -1; + } else { + /* I just can't believe there's no easy way to create + * a va_list. */ + char line[BIG_LINE]; + sprintf(line, templates[index].format, + (vars[0] != NULL) ? vars[0]: "", + (vars[1] != NULL) ? vars[1]: "", + (vars[2] != NULL) ? vars[2]: "", + (vars[3] != NULL) ? vars[3]: ""); + output(line); + } + } else { + /* We found a directive, but we can't identify it. Send it back.*/ + /* Check status first; if we're not supposed to be outputing */ + /* because of an "IF" block, don't tell the program to */ + /* try and cope with it. */ + if(status) + return -1; + else + return 0; + } + } else + /* We found no directive. The line is normal. */ + output(line_input); + + /* If we're here, we either handled it correctly or the line was benign.*/ + return 0; +} + +void conditional(char *input, char **vars, int index) +{ + if((!strncmp(templates[index].name, "IF", 2)) && + (vars[0] != NULL)) { + status = input[atoi(vars[0])] - '0'; + } else + if((!strncmp(templates[index].name, "ELSE", 4)) && + (status != -1)) { + status ^= 1; + } else + if(!strncmp(templates[index].name, "ENDIF", 5)) + status = -1; +} + +void respicker(char **config) +{ + output("<FORM action=rsrcpckr method=GET>\n"); + output("<hr size=4><center>\n"); + prevres(config); + output("</center><hr size=4>\n"); + output("</FORM>\n"); +} + +void currentres(char **config) +{ + int l; + char line[BIG_LINE]; + char *resname, *restype; + + resname = get_current_resource(config); + restype = get_current_typestr(config); + if(!strcmp(restype, NAME)) { + if(!strcmp(resname, "default")) + sprintf(line, "<font size=+1>Modifying: " + "<b>the entire server</b>.</font>"); + else + sprintf(line, "<font size=+1>Modifying: " + "<b>the object named %s</b>.</font>", resname); + } + else if(!strcmp(restype, FILE_OR_DIR)) { + l = strlen(resname) - 1; + if(resname[l] == '*') { + sprintf(line, "<font size=+1>Modifying: <b>the directory " + "%s</b></font>", + resname); + } else { + sprintf(line, "<font size=+1>Modifying: <b>%s %s</b></font>", + (strchr(resname, '*')) ? "files matching" : "the file", + resname); + } + } + else if(!strcmp(restype, TEMPLATE)) { + sprintf(line, "<font size=+1>Modifying: <b>the template %s</b></font>", + resname); + } + else if(!strcmp(restype, WILDCARD)) { +#ifdef MCC_PROXY + sprintf(line, "<font size=+1>Modifying: <b>URLs matching RE %s" +#else + sprintf(line, "<font size=+1>Modifying: <b>files matching %s" +#endif + "</b></font>", + resname); + } + output(line); +} + +void prevres(char **config) +{ +#ifndef MCC_NEWS + char *res = get_current_resource(config); + int rtype = get_current_restype(config); + + if(status) { + char **options = NULL; + register int x=0; + int found=0; + int option_cnt = total_object_count(); + + fprintf(stdout, "<SCRIPT language=JavaScript>\n"); + fprintf(stdout, "function checkForClick() {\n"); + fprintf(stdout, " document.forms[0].resource.blur();\n"); + fprintf(stdout, " var idx=document.forms[0]." + "resource.options.selectedIndex;\n"); + fprintf(stdout, " if(document.forms[0].resource." + "options[idx].defaultSelected == 0) {\n"); + fprintf(stdout, " document.forms[0].submit();\n"); + fprintf(stdout, " return 1;\n"); + fprintf(stdout, " } else return 0;\n"); + fprintf(stdout, "}\n"); + fprintf(stdout, "</SCRIPT>\n"); + +#ifdef MCC_PROXY + fprintf(stdout, "<TABLE BORDER=0>\n"); + fprintf(stdout, "<TR><TD><font size=+1>Editing:</font></TD>\n"); + fprintf(stdout, + "<TD><SELECT name=\"resource\" onChange=\"checkForClick()\" SIZE=\"%d\">\n", + option_cnt <= 20 ? 1 : 5); +#else + output("<nobr>"); + fputs("<font size=+1>Editing:</font>\n", stdout); + fprintf(stdout, "<SELECT name=\"resource\" " + "onChange=\"checkForClick()\" %s>\n", + option_cnt <=20 ? "" : "size=5"); +#endif + +#ifdef MCC_HTTPD /* template->styles nightmare */ + if((rtype==PB_NAME) && (strcmp(res, "default"))) { + /* enter: STYLES MODE */ + fprintf(stdout, "<OPTION value=ndefault>Exit styles mode\n"); + } else { + fprintf(stdout, "<OPTION value=ndefault %s>The entire server\n", + (!strcmp(res, "default")) ? "SELECTED" : ""); + } +#else + fprintf(stdout, "<OPTION value=ndefault %s>The entire server\n", + (!strcmp(res, "default")) ? "SELECTED" : ""); +#endif + if(!strcmp(res, "default")) found=1; + options = list_objects(PB_PATH); +#ifdef MCC_HTTPD /* template->styles nightmare */ + if((options) && !((rtype==PB_NAME) && (strcmp(res, "default"))) ) { +#else + if(options) { +#endif + for(x=0; options[x]; x++) { + fprintf(stdout, "<OPTION value=f%s %s>%s\n", + options[x], + (!strcmp(options[x], res)) ? "SELECTED" : "", + options[x]); + if(!strcmp(options[x], res)) found=1; + } + } + options=list_objects(PB_NAME); +#ifdef MCC_HTTPD /* template->styles nightmare */ + if((options) && ((rtype==PB_NAME) && (strcmp(res, "default"))) ) { +#else + if(options) { +#endif + for(x=0; options[x]; x++) { + if(!strcmp(options[x], "default") || + !strcmp(options[x], "cgi")) + continue; +#ifdef MCC_HTTPD /* template->style usability */ + fprintf(stdout, "<OPTION value=n%s %s>The style '%s'\n", + options[x], + (!strcmp(options[x], res)) ? "SELECTED":"", + options[x]); +#else + fprintf(stdout, "<OPTION value=n%s %s>The template '%s'\n", + options[x], + (!strcmp(options[x], res)) ? "SELECTED":"", + options[x]); +#endif + if(!strcmp(options[x], res)) found=1; + } + } + if(!found) { + if(rtype==PB_NAME) { + fprintf(stdout, "<OPTION value=n%s SELECTED>The template %s\n", + res, res); + } else { + fprintf(stdout, "<OPTION value=f%s SELECTED>%s\n", + res, res); + } + } + fputs("</SELECT></nobr>\n", stdout); + fputs("<nobr>", stdout); +#ifndef MCC_PROXY + fprintf(stdout, "<INPUT type=button value=\"Browse...\" " + "onClick=\"window.location='rsrcpckr?b'\"> "); +#endif +#ifdef MCC_PROXY + fprintf(stdout, "</TD>\n<TD>"); + fprintf(stdout, "<INPUT type=button value=\"Regular Expression...\" " + "onClick=\"var pat=" + "prompt('Enter the regular expression to edit:', ''); " +#else + fprintf(stdout, "<INPUT type=button value=\"Wildcard...\" " + "onClick=\"var pat=" + "prompt('Enter the wildcard pattern to edit:', ''); " +#endif + "if(pat!=null) window.location='rsrcpckr?" + "type="WILDCARD"&resource='+escape(pat);\">"); +#ifdef MCC_PROXY + fprintf(stdout, "</TD>\n</TR>\n</TABLE>\n"); +#endif + fputs("</nobr>", stdout); + /* output("</td></tr>\n"); */ + } +#endif +} + +void booktrack(char *input, char **vars) +{ + char line[BIG_LINE]; + + if((vars[0] != NULL) && (vars[1] != NULL)) { + sprintf(line, "<a href=index?0>" + "<img src=\"%s\" hspace=8 align=%s alt=\"\"></a>", + (input[0] - '0') ? vars[0] : vars[1], + (vars[2] != NULL) ? vars[2] : "none"); + output(line); + } +} + +void docswitcher(char *input) +{ + char line[BIG_LINE]; + char *whichimg, *whatmode; +#ifdef USE_ADMSERV + char *qs = getenv("QUERY_STRING"); + char *sname = getenv("SCRIPT_NAME"); + char *mtmp; + + char *tmp = getenv("SERVER_NAMES"); + char *servers = NULL; + if(tmp) servers = STRDUP(tmp); +#endif + + if(!(input[0] - '0')) { + whichimg = "b-clsd.gif"; + whatmode = "Express mode"; + } else { + whichimg = "b-open.gif"; + whatmode = "Full docs"; + } + + mtmp = (char *) MALLOC( (sname? strlen(sname) : 0) + + (qs? strlen(qs) : 0) + + (strlen(whichimg) + strlen(whatmode)) + + 1024); + sprintf(mtmp, "<center><table border=2 width=95%%>\n" + "<tr><td rowspan=2><a href=index%s>" + "<img src=\"../icons/%s\" " + "alt=\"[%s] \" border=2>" + "</td>\n", + (qs ? "?0" : sname), + whichimg, whatmode); + output(mtmp); + +#ifdef USE_ADMSERV + if(!servers) { + sprintf(line, "<td width=100%% align=center rowspan=2><b>%s</b></td>\n", + whatmode); + output(line); + } else + if(servers[0] == '(') { + + sprintf(line, "<td width=100%% align=center>Current servers:<br>\n"); + output(line); + output("<b>"); + + tmp=strtok(++servers, "|)"); + while(tmp) { + char *tmp2; + output("<nobr>"); + tmp2=strchr(tmp, '-'); + tmp2++; + output(tmp2); + tmp=strtok(NULL, "|)"); + if(tmp) + output(","); + output("</nobr>\n"); + } + output("</b></td>\n"); + } else { + + sprintf(line, "<td width=100%% align=center>Current server: "); + output(line); + output("<b>"); + tmp = strchr(servers, '-'); + *tmp++ = '\0'; + output(tmp); + output("</b>"); + output("</td>\n"); + } +#endif + sprintf(mtmp, "<td rowspan=2><a href=index%s>" + "<img src=\"../icons/%s\" " + "alt=\"\" border=2></a></td></tr>\n", + (qs? "?0" : sname), + whichimg); + output(mtmp); +#ifdef USE_ADMSERV + if(servers) { + sprintf(line, "<tr><td align=center>" + "<a href=\"/admin-serv/bin/chooser\">" + "Choose</a> a new server or set of servers</a></td>\n"); + output(line); + } +#endif + sprintf(line, "</tr></table></center>\n"); + output(line); + output("<hr width=10%%>\n"); +} + +void docroot(char **vars) +{ +#ifndef MCC_NEWS + char line[BIG_LINE]; + pblock *pb = grab_pblock(PB_NAME, "default", "NameTrans", "document-root", + NULL, NULL); + char *docroot = ""; + if(pb) + docroot = pblock_findval("root", pb); + sprintf(line, "<b>%s%s</b>\n", docroot, (vars[0] != NULL) ? vars[0] : ""); + output(line); +#endif +} + +void serverroot(char **vars) +{ + char line[BIG_LINE]; +#ifdef USE_ADMSERV + char *sroot = getenv("NETSITE_ROOT"); +#else + char *sroot = get_mag_var("#ServerRoot"); +#endif + sprintf(line, "%s%s", (sroot) ? sroot : "", (vars[0]) ? vars[0] : ""); + output(line); +} + +void makeurl(char **vars) +{ + char line[BIG_LINE]; + + sprintf(line,"<a href=%s target=_blank>%s</a>\n", + get_serv_url(), vars[0] ? vars[0] : ""); + output(line); +} + +void curservname(void) +{ + output(get_srvname(0)); +} + +NSAPI_PUBLIC +void pageheader(char **vars, char **config) +{ + char line[BIG_LINE]; +#if 0 /* MLM - put in to have non-working Back button */ + char *ref=get_referer(config); + char *t; +#endif + + output("<center><table border=2 width=100%%>\n"); + + util_snprintf(line, BIG_LINE, "<tr>"); + output(line); + + util_snprintf(line, BIG_LINE, "<td align=center width=100%%>"); + output(line); + util_snprintf(line, BIG_LINE, "<hr size=0 width=0>"); + output(line); +#if 0 /* MLM - put in to have non-working Back button */ + t=strrchr(ref, '/'); + *t++='\0'; + util_snprintf(line, BIG_LINE, "<a href=\"%s/index/%s\">", ref, t); + output(line); + util_snprintf(line, BIG_LINE, "<img align=right src=../icons/back.gif " + "width=41 height=26 border=0></a>\n"); + output(line); +#endif + util_snprintf(line, BIG_LINE, "<FONT size=+2><b>%s</b></FONT>" + "<hr size=0 width=0>" + "</td>", vars[2]); + output(line); + + output("</tr></table></center>\n"); +} + +char *_get_help_button(char *topic) +{ + char line[BIG_LINE]; + + util_snprintf( line, BIG_LINE, + "<input type=button value=\"%s\" " + "onClick=\"%s\">", XP_GetAdminStr(DBT_help_), + topic ? helpJavaScriptForTopic( topic ) : helpJavaScript() ); + + return(STRDUP(line)); +} + +NSAPI_PUBLIC char *helpJavaScriptForTopic( char *topic ) +{ + char *tmp; + char line[BIG_LINE]; + char *server=get_srvname(0); + char *type; + int typeLen; + + /* Get the server type, without the instance name into type */ + tmp = strchr( server, '-' ); + typeLen = tmp - server; + + type = (char *)MALLOC( typeLen + 1 ); + type[typeLen] = '\0'; + while ( typeLen-- ) { + type[typeLen] = server[typeLen]; + } + util_snprintf( line, BIG_LINE, + "if ( top.helpwin ) {" + " top.helpwin.focus();" + " top.helpwin.infotopic.location='%s/%s/admin/tutor?!%s';" + "} else {" + " window.open('%s/%s/admin/tutor?%s', '" + INFO_IDX_NAME"_%s', " + HELP_WIN_OPTIONS");}", + getenv("SERVER_URL"), server, topic, + getenv("SERVER_URL"), server, topic, + type ); + + return(STRDUP(line)); +} + +NSAPI_PUBLIC char *helpJavaScript() +{ + char *tmp, *sn; + + tmp=STRDUP(getenv("SCRIPT_NAME")); + if(strlen(tmp) > (unsigned)BIG_LINE) + tmp[BIG_LINE-2]='\0'; + sn=strrchr(tmp, '/'); + if( sn ) + *sn++='\0'; + return helpJavaScriptForTopic( sn ); +} + +void submit(int verify, char **vars) +{ + char line[BIG_LINE]; + char outline[BIG_LINE]; + + if(verify) { + util_snprintf(line, BIG_LINE, "<SCRIPT language="MOCHA_NAME">\n" + "function verify(form) {\n" + " if(confirm('Do you really want to %s?'))\n" + " form.submit();\n" + "}\n" + "</SCRIPT>\n", vars[0]); + output(line); + } + + output("<center><table border=2 width=100%%><tr>"); + + if(!verify) { + util_snprintf(outline, BIG_LINE, "%s%s%s%s%s", + "<td width=33%% align=center>", + "<input type=submit value=\"", + XP_GetAdminStr(DBT_ok_), + "\">", + "</td>\n"); + } else { + util_snprintf(outline, BIG_LINE, "%s%s%s%s%s%s", + "<td width=33%% align=center>", + "<input type=button value=\"", + XP_GetAdminStr(DBT_ok_), + "\" ", + "onclick=\"verify(this.form)\">", + "</td>\n"); + } + output(outline); + util_snprintf(outline, BIG_LINE, "%s%s%s%s", + "<td width=34%% align=center>", + "<input type=reset value=\"", + XP_GetAdminStr(DBT_reset_), + "\"></td>\n"); + output(outline); + + util_snprintf(line, BIG_LINE, "<td width=33%% align=center>%s</td>\n", + _get_help_button( vars[0] )); + output(line); + + output("</tr></table></center>\n"); + + output("</form>\n"); + + output("<SCRIPT language="MOCHA_NAME">\n"); + output("</SCRIPT>\n"); +} + +void helpbutton(char *topic) +{ + output("<form><p><div align=right><table width=33%% border=2>" + "<tr><td align=center>"); + output(_get_help_button(topic)); + output("</td></tr></table></div></form>\n"); + output("<SCRIPT language="MOCHA_NAME">\n"); + output("</SCRIPT>\n"); +} + +void dialogsubmit(char *topic) +{ + char line[BIG_LINE]; + char outline[BIG_LINE]; + + output("<center><table border=2 width=100%%><tr>"); + + util_snprintf(outline, BIG_LINE, "%s%s%s%s%s", + "<td width=33%% align=center>", + "<input type=submit value=\"", + XP_GetAdminStr(DBT_done_), + "\">", + "</td>\n"); + output(outline); + util_snprintf(outline, BIG_LINE, "%s%s%s%s%s", + "<td width=34%% align=center>", + "<input type=button value=\"", + XP_GetAdminStr(DBT_cancel_), + "\" " + "onClick=\"top.close()\"></td>\n"); + output(outline); + + util_snprintf(line, BIG_LINE, "<td width=33%% align=center>%s</td>\n", + _get_help_button(topic)); + output(line); + + output("</tr></table></center>\n"); + + output("</form>\n"); + + output("<SCRIPT language="MOCHA_NAME">\n"); + output("</SCRIPT>\n"); +} + +void helpjsfn(void) +{ + char *tmp; + char line[BIG_LINE]; + char *server=get_srvname(0); + char *type; + int typeLen; + + /* Get the server type, without the instance name into type */ + tmp = strchr( server, '-' ); + typeLen = tmp - server; + + type = (char *)MALLOC( typeLen + 1 ); + type[typeLen] = '\0'; + while ( typeLen-- ) { + type[typeLen] = server[typeLen]; + } + + output("function displayHelpTopic(topic)\n"); + output("{\n"); + util_snprintf(line, BIG_LINE, + " if (top.helpwin) {\n" + " top.helpwin.focus();\n" + " top.helpwin.infotopic.location='%s/%s/admin/tutor?!' + topic;\n" + " } else {\n" + " window.open('%s/%s/admin/tutor?' + topic, '" + INFO_IDX_NAME"_%s', " + HELP_WIN_OPTIONS");\n" + " }\n" + "}\n", + getenv("SERVER_URL"), server, + getenv("SERVER_URL"), server, + type ); + output(line); +} + +void link_referer(char **input, char **vars) +{ + char line[BIG_LINE]; + + sprintf( line, "<SCRIPT language="MOCHA_NAME">\n" + "document.writeln( '%s'.link( '%s' ) );\n" + "</SCRIPT>\n", ( vars[0] ? vars[0] : getenv( "SCRIPT_NAME" ) ), + cookieValue( "adminReferer", NULL ) ); + output( line ); +} + +int get_directive(char *string) +{ + int index = -1; + register int x; + + for(x=0; x < MAXTEMPLATE; x++) { + if(!strncmp(string, templates[x].name, + strlen(templates[x].name))) { + index = x; + break; + } + } + return index; +} + +NSAPI_PUBLIC int directive_is(char *target, char *directive) +{ + char *position = (target + strlen(DIRECTIVE_START)); + return(!(strncmp(directive, position, strlen(directive)))); +} + +char **get_vars(char *string) +{ + char **vars; + register int x; + int isvar; + char scratch[BIG_LINE]; + char lastchar; + +/* Initialize the vars array. + */ + vars = (char **) MALLOC((MAXVARS)*(sizeof(char *))); + for(x=0; x< MAXVARS; x++) + vars[x] = NULL; + + isvar = -1; + x = 0; + scratch[0] = '\0'; + lastchar = ' '; + + while(*string != '\0') { + if((*string == '\"') && (lastchar != '\\')) + if(isvar != -1) { + vars[x++] = (char *)STRDUP(scratch); + isvar = -1; + if(x == MAXVARS) + break; + } else + isvar = 0; + else + if(isvar != -1) { + scratch[isvar++] = *string; + scratch[isvar] = '\0'; + } + else + if(*string == DIRECTIVE_END) + break; + lastchar = *string; + string++; + } + return vars; +} + +static void output(char *line) +{ + if(status) + fputs(line, stdout); +} diff --git a/lib/libadmin/util.c b/lib/libadmin/util.c new file mode 100644 index 00000000..b560055d --- /dev/null +++ b/lib/libadmin/util.c @@ -0,0 +1,1340 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * util.c: Miscellaneous stuffs + * + * All blame to Mike McCool + */ + +#include "libadmin/libadmin.h" +#include "base/util.h" +#include "private/pprio.h" + +#ifdef XP_UNIX +#include <dirent.h> +#include <sys/types.h> +#include <fcntl.h> +#else +#include <base/file.h> +#include <sys/stat.h> +#endif /* WIN32? */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <ctype.h> /* isdigit */ + +#define NUM_ENTRIES 64 + +#ifdef MCC_PROXY +char * +XP_GetString() +{ + return "ZAP"; +} +#endif + +#ifdef XP_WIN32 +char *GetQueryNT(void) +{ + char *qs = getenv("QUERY_STRING"); + if(qs && (*qs == '\0')) + qs = NULL; + return qs; +} +#endif /* XP_WIN32 */ + +void escape_for_shell(char *cmd) { + register int x,y,l; + + l=strlen(cmd); + for(x=0;cmd[x];x++) { + if(strchr(" &;`'\"|*!?~<>^()[]{}$\\\x0A",cmd[x])){ + for(y=l+1;y>x;y--) + cmd[y] = cmd[y-1]; + l++; /* length has been increased */ + cmd[x] = '\\'; + x++; /* skip the character */ + } + } +} + +int _admin_dumbsort(const void *s1, const void *s2) +{ + return strcmp(*((char **)s1), *((char **)s2)); +} + +#ifdef XP_UNIX /* WIN32 change */ +/* Lists all files in a directory. */ +char **list_directory(char *path, int dashA) +{ + char **ar; + DIR *ds; + struct dirent *d; + int n, p; + + n = NUM_ENTRIES; + p = 0; + + ar = (char **) MALLOC(n * sizeof(char *)); + + if(!(ds = opendir(path))) { + return NULL; + } + + while( (d = readdir(ds)) ) { + if ( ( d->d_name[0] != '.' ) || + ( dashA && d->d_name[1] && + ( d->d_name[1] != '.' || d->d_name[2] ) ) ) { + if(p == (n-1)) { + n += NUM_ENTRIES; + ar = (char **) REALLOC(ar, n*sizeof(char *)); + } + /* 2: Leave space to add a trailing slash later */ + ar[p] = (char *) MALLOC(strlen(d->d_name) + 2); + strcpy(ar[p++], d->d_name); + } + } + closedir(ds); + + qsort((void *)ar, p, sizeof(char *), _admin_dumbsort); + ar[p] = NULL; + + return ar; +} + +#else /* WIN32 change */ +/* Lists all files in a directory. */ +char **list_directory(char *path, int dashA) +{ + char **ar; + SYS_DIR ds; + SYS_DIRENT *d; + int n, p; + + n = NUM_ENTRIES; + p = 0; + + ar = (char **) MALLOC(n * sizeof(char *)); + + if(!(ds = dir_open(path))) { + return NULL; + } + + while( (d = dir_read(ds)) ) { + if ( ( d->d_name[0] != '.' ) || + ( dashA && d->d_name[1] && + ( d->d_name[1] != '.' || d->d_name[2] ) ) ) { + if(p == (n-1)) { + n += NUM_ENTRIES; + ar = (char **) REALLOC(ar, n*sizeof(char *)); + } + /* 2: Leave space to add a trailing slash later */ + ar[p] = (char *) MALLOC(strlen(d->d_name) + 2); + strcpy(ar[p++], d->d_name); + } + } + dir_close(ds); + + qsort((void *)ar, p, sizeof(char *), _admin_dumbsort); + ar[p] = NULL; + + return ar; +} +#endif /* WIN32 */ + +int file_exists(char *fn) +{ + struct stat finfo; + + if(!stat(fn, &finfo)) + return 1; + else + return 0; +} + +int get_file_size(char *fn) +{ + struct stat finfo; + int ans = -1; + + if(!stat(fn, &finfo)) { + ans = finfo.st_size; + } else { + report_error(FILE_ERROR, fn, "Could not get size of file."); + } + return ans; +} + +int ADM_mkdir_p(char *dir, int mode) +{ + char path[PATH_MAX]; + struct stat fi; + char *slash = NULL; + + if (dir) + strcpy (path, dir); + else + return 0; + + if (slash = strchr(path, FILE_PATHSEP)) + slash++; /* go past root */ + else + return 0; + + while (slash && *slash) { + slash = strchr(slash, FILE_PATHSEP); + if (slash) *slash = '\0'; /* check path till here */ + + if (stat(path, &fi) == -1) { +#ifdef XP_UNIX + if (mkdir(path, mode) == -1) +#else /* XP_WIN32 */ + if (!CreateDirectory(path, NULL)) +#endif + return 0; + } + + if (slash) { + *slash = FILE_PATHSEP; /* restore path */ + slash++; /* check remaining path */ + } + } + return 1; +} + +int ADM_copy_directory(char *src_dir, char *dest_dir) +{ + SYS_DIR ds; + SYS_DIRENT *d; + struct stat fi; + char src_file[PATH_MAX], dest_file[PATH_MAX], fullname[PATH_MAX]; + + if (!(ds = dir_open(src_dir))) + report_error(FILE_ERROR, "Can't read directory", src_dir); + + while (d = dir_read(ds)) { + if (d->d_name[0] != '.') { + sprintf(fullname, "%s/%s", src_dir, d->d_name); + if (system_stat(fullname, &fi) == -1) + continue; + + sprintf(src_file, "%s%c%s", src_dir, FILE_PATHSEP, d->d_name); + sprintf(dest_file, "%s%c%s", dest_dir, FILE_PATHSEP, d->d_name); + if (S_ISDIR(fi.st_mode)) { + char *sub_src_dir = STRDUP(src_file); + char *sub_dest_dir = STRDUP(dest_file); + if (!ADM_mkdir_p(sub_dest_dir, 0755)) { + report_error(FILE_ERROR, "Cannot create directory", + sub_dest_dir); + return 0; + } + if (!ADM_copy_directory(sub_src_dir, sub_dest_dir)) + return 0; + FREE(sub_src_dir); + FREE(sub_dest_dir); + } + else + cp_file(src_file, dest_file, 0644); + } + } + dir_close(ds); + return(1); +} + +void ADM_remove_directory(char *path) +{ + struct stat finfo; + char **dirlisting; + register int x=0; + int stat_good = 0; + char *fullpath = NULL; + +#ifdef XP_UNIX + stat_good = (lstat(path, &finfo) == -1 ? 0 : 1); +#else /* XP_WIN32 */ + stat_good = (stat(path, &finfo) == -1 ? 0 : 1); +#endif + + if(!stat_good) return; + + if(S_ISDIR(finfo.st_mode)) { + dirlisting = list_directory(path,1); + if(!dirlisting) return; + + for(x=0; dirlisting[x]; x++) { + fullpath = (char *) MALLOC(strlen(path) + + strlen(dirlisting[x]) + 4); + sprintf(fullpath, "%s%c%s", path, FILE_PATHSEP, dirlisting[x]); +#ifdef XP_UNIX + stat_good = (lstat(fullpath, &finfo) == -1 ? 0 : 1); +#else /* XP_WIN32 */ + stat_good = (stat(fullpath, &finfo) == -1 ? 0 : 1); +#endif + if(!stat_good) continue; + if(S_ISDIR(finfo.st_mode)) { + ADM_remove_directory(fullpath); + } else { + unlink(fullpath); + } + FREE(fullpath); + } +#ifdef XP_UNIX + rmdir(path); +#else /* XP_WIN32 */ + RemoveDirectory(path); +#endif + } else { + delete_file(path); + } + return; +} + +/* return: mtime(f1) < mtime(f2) ? */ +int mtime_is_earlier(char *file1, char *file2) +{ + struct stat fi1, fi2; + + if(stat(file1, &fi1)) { + return -1; + } + if(stat(file2, &fi2)) { + return -1; + } + return( (fi1.st_mtime < fi2.st_mtime) ? 1 : 0); +} + +time_t get_mtime(char *fn) +{ + struct stat fi; + + if(stat(fn, &fi)) + return 0; + return fi.st_mtime; +} + +int all_numbers(char *target) +{ + register int x=0; + + while(target[x]) + if(!isdigit(target[x++])) + return 0; + return 1; +} + + +int all_numbers_float(char *target) +{ + register int x; + int seenpt; + + for(x = 0, seenpt = 0; target[x]; ++x) { + if((target[x] == '.') && (!seenpt)) + seenpt = 1; + else if((!isdigit(target[x])) && seenpt) + return 0; + } + return 1; +} + +/* Get the admin/config directory. */ +char *get_admcf_dir(int whichone) +{ +#ifdef USE_ADMSERV + char *confdir = NULL; + + char *tmp = get_num_mag_var(whichone, "#ServerRoot"); + if(!tmp) { + /* sigh */ + report_error(INCORRECT_USAGE, "No server root variable", + "The magnus.conf variable #ServerRoot was " + "not set. Please set the value of your server " + "root through the administrative forms."); + } + confdir = (char *) MALLOC(strlen(tmp) + strlen("config") + 4); + sprintf(confdir, "%s%cconfig%c", tmp, FILE_PATHSEP, FILE_PATHSEP); + + return confdir; +#else + char *confdir; + char line[BIG_LINE]; + sprintf(line, "%s%cadmin%cconfig%c", get_mag_var("#ServerRoot"), + FILE_PATHSEP, FILE_PATHSEP, FILE_PATHSEP); + confdir = STRDUP(line); +#endif + return STRDUP(confdir); +} + +/* Get the current HTTP server URL. */ +char *get_serv_url(void) +{ +#ifdef USE_ADMSERV + char *name = get_mag_var("ServerName"); + char *port = get_mag_var("Port"); + char *protocol = NULL; + char line[BIG_LINE]; + +#ifndef NS_UNSECURE + char *security = get_mag_var("Security"); + + if(!security || strcasecmp(security, "on")) { + protocol = STRDUP("http"); + if(!strcmp(port, "80")) + port = STRDUP(""); + else { + sprintf(line, ":%s", port); + port = STRDUP(line); + } + } else { + protocol = STRDUP("https"); + if(!strcmp(port, "443")) + port = STRDUP(""); + else { + sprintf(line, ":%s", port); + port = STRDUP(line); + } + } +#else + protocol = STRDUP("http"); + if(!strcmp(port, "80")) + port = STRDUP(""); + else { + sprintf(line, ":%s", port); + port = STRDUP(line); + } +#endif + + sprintf(line, "%s://%s%s", protocol, name, port); + return(STRDUP(line)); +#else + return(getenv("SERVER_URL")); +#endif +} + +/* ------------------------------- run_cmd -------------------------------- */ + + +/* Previously in install. This is also pretty UNIX-ish. */ + +/* Nirmal: Added code for Win32 implementation of this function. */ + +#include <signal.h> +#ifdef XP_UNIX +#include <sys/wait.h> +#endif /* XP_UNIX */ + + +int run_cmd(char *cmd, FILE *closeme, struct runcmd_s *rm) +{ +#ifdef WIN32 + HANDLE hproc; + PROCESS_INFORMATION child; + STARTUPINFO siStartInfo ; +#else + struct stat fi; + int exstat; + char *errmsg, tfn[128]; + FILE *f; + int fd; + pid_t pid; +#endif + + +#ifdef WIN32 + /* Nirmal: + For now, i will just spawn + a child in WINNT to execute the command. Communication to + the parent is done through stdout pipe, that was setup by + the parent. + + */ + hproc = OpenProcess(STANDARD_RIGHTS_REQUIRED, FALSE, GetCurrentProcessId()); + if (hproc == NULL) { + fprintf(stdout, "Content-type: text/html\n\n"); + fflush(stdout); + report_error(SYSTEM_ERROR, NULL, "Could not open handle to myself"); + return -1; // stmt. not reached. + } + + ZeroMemory(&child, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.lpReserved = siStartInfo.lpReserved2 = NULL; + siStartInfo.cbReserved2 = 0; + siStartInfo.lpDesktop = NULL; + siStartInfo.dwFlags = STARTF_USESHOWWINDOW; +// Several fields arent used when dwFlags is not set. +// siStartInfo.hStdInput = hChildStdinRd; +// siStartInfo.hStdOutput = siStartInfo.hStdError = hChildStdoutWr; + siStartInfo.wShowWindow = SW_HIDE; + + if ( ! CreateProcess( + NULL, // pointer to name of executable module + cmd, // pointer to command line string + NULL, // pointer to process security attribute + NULL, // pointer to thread security attributes + TRUE, // handle inheritance flag + 0, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &siStartInfo, // pointer to STARTUPINFO + &child // pointer to PROCESS_INFORMATION + )) + { + rm->title = "CreateProcess failed"; + rm->msg = "run_cmd: Can't create new process. "; + rm->arg = ""; + rm->sysmsg = 1; + return -1; + } + else + return 0; +#else + sprintf(cmd, "%s > /tmp/startmsg.%d 2>&1 < /dev/null", cmd, getpid()); /* */ + /* FUCK UNIX SIGNALS. */ + signal(SIGCHLD, SIG_DFL); + switch( (pid = fork()) ) { + case 0: + /* Hmm. Work around an apparent bug in stdio. */ + if(closeme) + close(fileno(closeme)); + execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL); + /* DOH! */ + sprintf(tfn, "/tmp/startmsg.%d", getpid()); + if(!(f = fopen(tfn, "w"))) + exit(1); + fprintf(f, "Exec of %s failed. The error was %s.\n", cmd, + system_errmsg()); + fclose(f); + exit(1); + case -1: + rm->title = "Fork failed"; + rm->msg = "Can't create new process. %s"; + rm->arg = ""; + rm->sysmsg = 1; + return -1; + default: + sprintf(tfn, "/tmp/startmsg.%d", getpid()); + + if(waitpid(pid, &exstat, 0) == -1) { + rm->title = "Can't wait for child"; + rm->msg = "Can't wait for process. %s"; + rm->arg = ""; + rm->sysmsg = 1; + return -1; + } + if(exstat) { + if(!(fd = open(tfn, O_RDONLY))) { + rm->title = "Can't open error file"; + rm->msg = "Can't find error file %s."; + rm->arg = cmd; + rm->sysmsg = 1; + return -1; + } + fstat(fd, &fi); + if((fi.st_size > 0) && (fi.st_size < 8192)) { + errmsg = (char *) MALLOC(fi.st_size + 1); + read(fd, errmsg, fi.st_size); + errmsg[fi.st_size] = '\0'; + close(fd); + unlink(tfn); + rm->title = "Command execution failed"; + rm->msg = "The command did not execute. " + "Here is the output:<p>\n<pre>\n%s\n</pre>\n"; + rm->arg = errmsg; + rm->sysmsg = 0; + return -1; + } + else { + close(fd); + unlink(tfn); + + rm->title = "Command execution failed"; + rm->msg = "The command didn't execute, and it did not produce " + "any output. Run <code>%s</code> from the command " + "line and examine the output.\n"; + rm->arg = cmd; + rm->sysmsg = 0; + return -1; + } + } + unlink(tfn); + return 0; + } +#endif /* WIN32 */ + +} + + + +/* This is basically copy_file from the install section, with the error + * reporting changed to match the admin stuff. Since some stuff depends + * on copy_file being the install version, I'll cheat and call this one + * cp_file. */ +#ifdef XP_UNIX + +#define COPY_BUFFER_SIZE 4096 + +void cp_file(char *sfile, char *dfile, int mode) +{ + int sfd, dfd, len; + struct stat fi; + + char copy_buffer[COPY_BUFFER_SIZE]; + unsigned long read_len; + +/* Make sure we're in the right umask */ + umask(022); + + if( (sfd = open(sfile, O_RDONLY)) == -1) + report_error(FILE_ERROR, sfile, "Can't open file for reading."); + + fstat(sfd, &fi); + if(!(S_ISREG(fi.st_mode))) { + close(sfd); + return; + } + len = fi.st_size; + + if( (dfd = open(dfile, O_RDWR | O_CREAT | O_TRUNC, mode)) == -1) + report_error(FILE_ERROR, dfile, "Can't write to file."); + + while(len) { + read_len = len>COPY_BUFFER_SIZE?COPY_BUFFER_SIZE:len; + + if ( (read_len = read(sfd, copy_buffer, read_len)) == -1) { + report_error(FILE_ERROR, sfile, "Error reading file for copy."); + } + + if ( write(dfd, copy_buffer, read_len) != read_len) { + report_error(FILE_ERROR, dfile, "Error writing file for copy."); + } + + len -= read_len; + } + close(sfd); + close(dfd); +} + +#else /* XP_WIN32 */ +void cp_file(char *sfile, char *dfile, int mode) +{ + HANDLE sfd, dfd, MapHandle; + PCHAR fp; + DWORD BytesWritten = 0; + DWORD len; + + if( (sfd = CreateFile(sfile, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) + == INVALID_HANDLE_VALUE) { + report_error(FILE_ERROR, "Cannot open file for reading", sfile); + } + len = GetFileSize(sfd, NULL); + if( (dfd = CreateFile(dfile, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { + report_error(FILE_ERROR, "Cannot open destination file for writing", + dfile); + } + if (len == 0) + return; + if( (MapHandle = CreateFileMapping(sfd, NULL, PAGE_READONLY, + 0, 0, NULL)) == NULL) { + report_error(FILE_ERROR, "Cannot create file mapping", sfile); + } + if (!(fp = MapViewOfFile(MapHandle, FILE_MAP_READ, 0, 0, 0))) { + report_error(FILE_ERROR, "Cannot map file %s", sfile); + } + while ( len) { + if(!WriteFile(dfd, fp, len, &BytesWritten, NULL)) { + report_error(FILE_ERROR, "Cannot write new file", dfile); + } + len -= BytesWritten; + fp += BytesWritten; + } + + CloseHandle(sfd); + UnmapViewOfFile(fp); + CloseHandle(MapHandle); + FlushFileBuffers(dfd); + CloseHandle(dfd); +} +#endif + +int delete_file(char *path) +{ +#ifdef XP_UNIX + return unlink(path); +#else + return !(DeleteFile(path)); +#endif +} + +void create_dir(char *dir, int mode) +{ + if ((dir == (char *) NULL) || (strlen(dir) == 0)) { + report_error(FILE_ERROR, "No directory is specified", + "Could not create a necessary directory."); + } + + if(!file_exists(dir)) { +#ifdef XP_UNIX + if(mkdir(dir, mode) == -1) { +#else /* XP_WIN32 */ + if(!CreateDirectory(dir, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) +#endif /* XP_WIN32 */ + report_error(FILE_ERROR, dir, + "Could not create a necessary directory."); + } + } +} + +#ifdef XP_UNIX +SYS_FILE lf; +#elif defined(XP_WIN32) +HANDLE lf; +#endif + +char *get_flock_path(void) +{ + char *result=""; + char *port=getenv("SERVER_PORT"); +#ifdef XP_UNIX + result=(char *) MALLOC(strlen("/tmp/lock.%%s.")+strlen(port)+4); + sprintf(result, "/tmp/lock.%%s.%s", port); +#endif + return result; +} + +/* Open a file with locking, close a file with unlocking. */ +FILE *fopen_l(char *path, char *mode) +{ + FILE *f = fopen(path, mode); + char *lockpath; + char *sn=get_srvname(0); + char *flp=FILE_LOCK_PATH; + + if(f == NULL) return NULL; + lockpath=(char *) MALLOC(strlen(sn)+strlen(flp)+16); + sprintf(lockpath, flp, sn); +#ifdef XP_UNIX + if( (lf=system_fopenRW(lockpath)) == SYS_ERROR_FD) + report_error(FILE_ERROR, lockpath, "Could not open file."); + if(system_flock(lf)==IO_ERROR) + report_error(FILE_ERROR, lockpath, "Could not lock file."); +#elif defined(XP_WIN32) + /* Using mutexes because if the CGI program dies, the mutex will be + * automatically released by the OS for another process to grab. + * Semaphores do not have this property; and if the CGI program crashes, + * the admin server would be effectively crippled. + */ + if ( (lf = CreateMutex(NULL, 0, lockpath)) == NULL) { + report_error(FILE_ERROR, lockpath, "Could not create admin mutex."); + } else { + if ( WaitForSingleObject(lf, 60*1000) == WAIT_FAILED) { + report_error(FILE_ERROR, lockpath, "Unable to obtain mutex after 60 seconds."); + } + } +#endif /* XP_UNIX */ + return f; +} + +void fclose_l(FILE *f) +{ + fclose(f); +#ifdef XP_UNIX + if(system_ulock(lf)==IO_ERROR) + report_error(FILE_ERROR, NULL, "Could not unlock lock file."); + system_fclose(lf); +#elif defined(XP_WIN32) + if (lf) { + ReleaseMutex(lf); + CloseHandle(lf); + } +#endif /* XP_UNIX */ +} + +/* Ripped off from the client. (Sorry, Lou.) */ +/* */ +/* The magic set of 64 chars in the uuencoded data */ +unsigned char uuset[] = { +'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', +'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n', +'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7', +'8','9','+','/' }; + +int do_uuencode(unsigned char *src, unsigned char *dst, int srclen) +{ + int i, r; + unsigned char *p; + +/* To uuencode, we snip 8 bits from 3 bytes and store them as +6 bits in 4 bytes. 6*4 == 8*3 (get it?) and 6 bits per byte +yields nice clean bytes + +It goes like this: + AAAAAAAA BBBBBBBB CCCCCCCC +turns into the standard set of uuencode ascii chars indexed by numbers: + 00AAAAAA 00AABBBB 00BBBBCC 00CCCCCC + +Snip-n-shift, snip-n-shift, etc.... + +*/ + + for (p=dst,i=0; i < srclen; i += 3) { + /* Do 3 bytes of src */ + register char b0, b1, b2; + + b0 = src[0]; + if (i==srclen-1) + b1 = b2 = '\0'; + else if (i==srclen-2) { + b1 = src[1]; + b2 = '\0'; + } + else { + b1 = src[1]; + b2 = src[2]; + } + + *p++ = uuset[b0>>2]; + *p++ = uuset[(((b0 & 0x03) << 4) | ((b1 & 0xf0) >> 4))]; + *p++ = uuset[(((b1 & 0x0f) << 2) | ((b2 & 0xc0) >> 6))]; + *p++ = uuset[b2 & 0x3f]; + src += 3; + } + *p = 0; /* terminate the string */ + r = (unsigned char *)p - (unsigned char *)dst;/* remember how many we did */ + + /* Always do 4-for-3, but if not round threesome, have to go + clean up the last extra bytes */ + + for( ; i != srclen; i--) + *--p = '='; + + return r; +} + +char *alert_word_wrap(char *str, int width, char *linefeed) +{ + char *ans = NULL; + int counter=0; + int lsc=0, lsa=0; + register int strc=0, ansc=0; + register int x=0; + + /* assume worst case */ + ans = (char *) MALLOC((strlen(str)*strlen(linefeed))+32); + + for(strc=0, ansc=0; str[strc]; /*none*/) { + if(str[strc]=='\n') { + counter=0; + lsc=0, lsa=0; + for(x=0; linefeed[x]; x++) { + ans[ansc++]=linefeed[x]; + } + strc++; + } else if(str[strc]=='\r') { + strc++; + } else if(str[strc]=='\\') { + ans[ansc++]='\\'; + ans[ansc++]=strc++; + } else { + if(counter==width) { + if(lsc && lsa) { + strc=lsc; + ansc=lsa; + + counter=0; + lsc=0, lsa=0; + for(x=0; linefeed[x]; x++) { + ans[ansc++]=linefeed[x]; + } + strc++; + } else { + /* else, you're a loser, I'm breaking your big word anyway */ + counter=0; + lsc=0, lsa=0; + for(x=0; linefeed[x]; x++) { + ans[ansc++]=linefeed[x]; + } + strc++; + } + } else { + if(str[strc] == ' ') { + lsc=strc; + lsa=ansc; + } + ans[ansc++]=str[strc++]; + counter++; + } + } + } + ans[ansc]='\0'; + return ans; +} + +void remove_directory(char *path) +{ + struct stat finfo; + char **dirlisting; + register int x=0; + int stat_good = 0; + char *fullpath = NULL; + +#ifdef XP_UNIX + stat_good = (lstat(path, &finfo) == -1 ? 0 : 1); +#else /* WIN32 */ + stat_good = (stat(path, &finfo) == -1 ? 0 : 1); +#endif /* XP_UNIX */ + + if(!stat_good) return; + + if(S_ISDIR(finfo.st_mode)) { + dirlisting = list_directory(path,1); + if(!dirlisting) return; + + for(x=0; dirlisting[x]; x++) { + fullpath = (char *) MALLOC(strlen(path) + + strlen(dirlisting[x]) + 4); + sprintf(fullpath, "%s%c%s", path, FILE_PATHSEP, dirlisting[x]); +#ifdef XP_UNIX + stat_good = (lstat(fullpath, &finfo) == -1 ? 0 : 1); +#else /* WIN32 */ + stat_good = (stat(fullpath, &finfo) == -1 ? 0 : 1); +#endif /* XP_UNIX */ + if(!stat_good) continue; + if(S_ISDIR(finfo.st_mode)) { + remove_directory(fullpath); + } else { + fprintf(stdout, "<i>Removing file</i> " + "<code>%s</code><br>\n", fullpath); + unlink(fullpath); + } + FREE(fullpath); + } + fprintf(stdout, "<i>Removing directory</i> " + "<code>%s</code><br>\n", path); +#ifdef XP_UNIX + rmdir(path); +#else /* XP_WIN32 */ + RemoveDirectory(path); +#endif /* XP_WIN32 */ + } else { + fprintf(stdout, "<i>Removing file</i> <code>%s</code><br>\n", path); + unlink(path); + } + return; +} + +int str_flag_to_int(char *str_flag) +{ + if (!str_flag) + return -1; + if (!strcmp(str_flag, "1")) + return 1; + return 0; +} + +/* + * get_ip_and_mask - function to take something that may be an IP Address + * and netmaks, and validate it. It takes two possible + * + * Parmaters: char *candidate + * + * Returns NULL if it isn't a valid IP address and mask. It returns + * the IP address and mask in the form "iii.iii.iii.iii mmm.mmm.mmm.mmm" + * if it is valid. This is in a string dynamicly allocated in this + * function. + * + * Processing: the candidate is assumed to be in one of + * these two formats: + * + * 1. "iii.iii.iii.iii" (returns: "iii.iii.iii.iii 255.255.255.255") + * 2. "iii.iii.iii.iii mmm.mmm.mmm.mmm" + * 3. "iii.*", "iii.iii.*", or "iii.iii.iii.*" + * + * The rules are: + * I. If it has a space in it, it is assumed to be the delimiter in + * format 2. + * II. If it has a "*" in it, it's assumed to be format 3. + * III. If it's in format 3, the net mask returned is: + * 255.0.0.0, 255.255.0.0, or 255.255.255.0 respectivly, + * and parts of the address right of the * is replaced with 0s. + * IV. We use inet_addr on the pieces to validate them. + * + * + */ + +char *get_ip_and_mask(char *candidate) +{ + char work[BIG_LINE]; + + char *p; + char *result = NULL; + int len; + int dots = 0; + int i; + + if (candidate && strlen(candidate) < (unsigned) BIG_LINE) { + + if ((p = strchr(candidate, ' '))) { + len = p-candidate+1; + memcpy(work, candidate, len); + work[len] = '\0'; + if (inet_addr(work) != -1) { + len = strlen(candidate)-strlen(p)-1; + if (len > 0) { + memcpy(work, p+1, len); + work[len] = '\0'; + if (inet_addr(work) != -1) { + result = strdup(candidate); + } + } + } + } + else if ((p = strchr(candidate, '*')) && + (p-candidate > 1 ) && + (*(p-1) == '.') ) { + memset(work, 0, BIG_LINE); + for (i=0; candidate[i]!='*'; ++i) { + if (candidate[i+1] != '*') + work[i] = candidate[i]; + if (candidate[i] == '.') + ++dots; + } + if (dots == 1 || dots == 2 || dots == 3) { + for (i=0; i<4-dots; ++i) { + strcat(work, ".0"); + } + if (inet_addr(work) != -1) { + strcat(work, " "); + p = &work[strlen(work)]; + for (i=0; i<dots; ++i) { + if (i==0) + strcat(work, "255"); + else + strcat(work, ".255"); + } + for (i=0; i<4-dots; ++i) { + strcat(work, ".0"); + } + if (inet_addr(p) != -1) { + result = strdup(work); + } + } + } + } + else { + if (inet_addr(candidate) != -1) { + strcpy(work, candidate); + strcat(work, " 255.255.255.255"); + result = strdup(work); + } + } + } + else + result = NULL; + + return result; +} + +/* do fgets with a filebuffer *, instead of a File *. Can't use util_getline + because it complains if the line is too long. + It does throw away <CR>s, though. + */ +NSAPI_PUBLIC char *system_gets( char *line, int size, filebuffer *fb ) +{ + int c; + int i = 0; + + while ( --size ) { + switch ( c = filebuf_getc( fb ) ) { + case IO_EOF: + line[i] = '\0'; + return i ? line : NULL; + case LF: + line[i] = c; + line[i+1] = '\0'; + return line; /* got a line, and it fit! */ + case IO_ERROR: + return NULL; + case CR: + ++size; + break; + default: + line[i++] = c; + break; + } + } + /* if we got here, then we overran the buffer size */ + line[i] = '\0'; + return line; +} + +#ifndef WIN32 + +/* make a zero length file, no matter how long it was before */ +NSAPI_PUBLIC int +system_zero( SYS_FILE f ) +{ + ftruncate( PR_FileDesc2NativeHandle( f ), 0 ); + return 0; +} + +#endif + +/*********************************************************************** +** FUNCTION: cookieValue +** DESCRIPTION: +** Get the current value of the cookie variable +** INPUTS: var - the name of the cookie variable +** val - if non-NULL, set the in-memory copy of the var +** OUTPUTS: None +** RETURN: NULL if the var doesn't exist, else the value +** SIDE EFFECTS: +** Eats memory +** RESTRICTIONS: +** Don't screw around with the returned string, if anything else wants +** to use it. +** MEMORY: This is a memory leak, so only use it in CGIs +** ALGORITHM: +** If it's never been called, build a memory structure of the +** cookie variables. +** Look for the passed variable, and return its value, or NULL +***********************************************************************/ + +NSAPI_PUBLIC char * +cookieValue( char *var, char *val ) +{ + static char **vars = NULL; + static char **vals = NULL; + static int numVars = -1; + int i; + + if ( numVars == -1 ) { /* first time, init the structure */ + char *cookie = getenv( "HTTP_COOKIE" ); + + if ( cookie && *cookie ) { + int len = strlen( cookie ); + int foundVal = 0; + + cookie = STRDUP( cookie ); + numVars = 0; + vars = (char **)MALLOC( sizeof( char * ) ); + vals = (char **)MALLOC( sizeof( char * ) ); + vars[0] = cookie; + for ( i = 0 ; i < len ; ++i ) { + if ( ( ! foundVal ) && ( cookie[i] == '=' ) ) { + vals[numVars++] = cookie + i + 1; + cookie[i] = '\0'; + foundVal = 1; + } else if ( ( cookie[i] == ';' ) && ( cookie[i+1] == ' ' ) ) { + cookie[i] = '\0'; + vals = (char **) REALLOC( vals, + sizeof( char * ) * ( numVars + 1 ) ); + vars = (char **) REALLOC( vars, + sizeof( char * ) * ( numVars + 1 ) ); + vars[numVars] = cookie + i + 2; + i += 2; + foundVal = 0; + } + } + } else { /* no cookie, no vars */ + numVars = 0; + } + } + for ( i = 0 ; i < numVars ; ++i ) { + if ( strcmp( vars[i], var ) == 0 ) { + if ( val ) { + vals[i] = STRDUP( val ); + } else { + return vals[i]; + } + } + } + return NULL; +} + +/*********************************************************************** +** FUNCTION: jsEscape +** DESCRIPTION: +** Escape the usual suspects, so the parser javascript parser won't eat them +** INPUTS: src - the string +** OUTPUTS: NONE +** RETURN: A malloced string, containing the escaped src +** SIDE EFFECTS: +** None, except for more memory being eaten +** RESTRICTIONS: +** None +** MEMORY: One Malloc, you should free this if you care +***********************************************************************/ + +NSAPI_PUBLIC char * +jsEscape( char *src ) +{ + int needsEscaping = 0; + int i; + char *dest; + + for ( i = 0 ; src[i] ; ++i ) { + if ( src[i] == '\\' || src[i] == '\'' || src[i] == '"' ) { + ++needsEscaping; + } + } + dest = (char *)MALLOC( i + needsEscaping + 1 ); + for ( i = 0 ; *src ; ++src ) { + if ( ( *src == '\\' ) || ( *src == '\'' ) || ( *src == '"' ) ) { + dest[i++] = '\\'; /* escape */ + } + dest[i++] = *src; + } + dest[i] = '\0'; + return dest; +} + +/*********************************************************************** +** FUNCTION: jsPWDialogSrc +** DESCRIPTION: +** Put the source to the passwordDialog JavaScript function out. +** INPUTS: inScript - if true, don't put <SCRIPT> stuff out +** otherJS - if nonNULL, other javascript to execute +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** clogs up stdout +** RESTRICTIONS: +** Don't use this outside of a CGI, or before the Content-type: +** MEMORY: No memory change +** ALGORITHM: +** @+@What's really going on? +***********************************************************************/ + +NSAPI_PUBLIC void +jsPWDialogSrc( int inScript, char *otherJS ) +{ + static int srcSpewed = 0; + + otherJS = otherJS ? otherJS : ""; + + if ( ! inScript ) { + fprintf( stdout, "<SCRIPT LANGUAGE=\""MOCHA_NAME"\">\n" ); + } + if ( ! srcSpewed ) { + srcSpewed = 1; + fprintf( stdout, "function passwordDialog( prompt, element ) {\n" + " var dlg = window.open( '', 'dialog', 'height=60,width=500' );\n" + " dlg.document.write(\n" + " '<form name=f1 onSubmit=\"opener.document.'\n" + " + element + '.value = goo.value; window.close(); " + "%s; return false;\">',\n" + " prompt, '<input type=password name=goo></form>' );\n" + " dlg.document.f1.goo.focus();\n" + " dlg.document.close();\n" + "}\n", otherJS ); + } + if ( ! inScript ) { + fprintf( stdout, "</SCRIPT>\n" ); + } +} + +static int adm_initialized=0; + +/* Initialize NSPR for all the base functions we use */ +NSAPI_PUBLIC int ADM_Init(void) +{ + if(!adm_initialized) { + NSPR_INIT("AdminPrograms"); + adm_initialized=1; + } + return 0; +} + + +#ifdef XP_UNIX +/* + * This function will return the SuiteSpot user id and group id used to + * recommend that Netscape Servers to run as. Any line starts with '#' + * is treated as comment. It looks for SuiteSpotUser/SuiteSpotGroup + * name/value pair. + * + * It returns 0 when success and allocate storage for user and group. + * returns -1 when only SuiteSpot user id is found. + * returns -2 when only SuiteSpot group id is found. + * returns -3 when NO SuiteSpot user and group is found. + * returns -4 when fails to open <server_root>/install/ssusers.conf + */ +NSAPI_PUBLIC int ADM_GetUXSSid(char *sroot, char **user, char **group) +{ + int foundUser, foundGroup; + char fn[BIG_LINE]; + char line[BIG_LINE]; + FILE *f; + + foundUser = 0; + foundGroup = 0; + *user = (char *) NULL; + *group = (char *) NULL; + + sprintf(fn, "%s/install/ssusers.conf", sroot); + if (f = fopen(fn, "r")) { + while (fgets(line, sizeof(line), f)) { + if (line[0] == '#') { + continue; + } + if (!strncmp(line, "SuiteSpotUser", strlen("SuiteSpotUser"))) { + char *ptr1; + ptr1 = line + strlen("SuiteSpotUser"); + while ((*ptr1 == '\t') || (*ptr1 == ' ')) { + ptr1++; + } + if ((strlen(ptr1) > 0) && (*user == (char *) NULL)) { + *user = (char *) MALLOC(strlen(ptr1)+1); + if (ptr1[strlen(ptr1)-1] == '\n') { + ptr1[strlen(ptr1)-1] = '\0'; + } + strcpy(*user, ptr1); + } + foundUser = 1; + continue; + } + if (!strncmp(line, "SuiteSpotGroup", strlen("SuiteSpotGroup"))) { + char *ptr1; + ptr1 = line + strlen("SuiteSpotGroup"); + while ((*ptr1 == '\t') || (*ptr1 == ' ')) { + ptr1++; + } + if ((strlen(ptr1) > 0) && (*group == (char *) NULL)) { + *group = (char *) MALLOC(strlen(ptr1)+1); + if (ptr1[strlen(ptr1)-1] == '\n') { + ptr1[strlen(ptr1)-1] = '\0'; + } + strcpy(*group, ptr1); + } + foundGroup = 1; + continue; + } + } + fclose(f); + } else { + return(-4); + } + + if (foundUser && foundGroup) { + return(0); + } else if (foundUser) { + return(-1); + } else if (foundGroup) { + return(-2); + } else { + return(-3); + } +} +#endif /* XP_UNIX */ diff --git a/lib/libnt/Makefile b/lib/libnt/Makefile new file mode 100644 index 00000000..2f12fe5b --- /dev/null +++ b/lib/libnt/Makefile @@ -0,0 +1,27 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +#! gmake + +DEPTH = ../.. + +CSRCS = info.c \ + path.c \ + pmddeml.c \ + registry.c \ + service.c \ + tcpip.c \ + $(NULL) + +OBJS = $(CSRCS:.c=.o) + +LIBRARY = libnt.$(LIB_SUFFIX) + +include $(DEPTH)/config/rules.mk + +export:: $(TARGETS) + $(INSTALL) -m 444 $(LIBRARY) $(DIST)/lib diff --git a/lib/libnt/info.c b/lib/libnt/info.c new file mode 100644 index 00000000..618b95c7 --- /dev/null +++ b/lib/libnt/info.c @@ -0,0 +1,81 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <windows.h> +#include "nt/ntos.h" + +OS_TYPE NS_WINAPI INFO_GetOperatingSystem () +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx( &versionInfo ); + + switch ( versionInfo.dwPlatformId ) { + case VER_PLATFORM_WIN32s: + return OS_WIN32S; + case VER_PLATFORM_WIN32_WINDOWS: + return OS_WIN95; + case VER_PLATFORM_WIN32_NT: + return OS_WINNT; + default: + break; + } + return OS_UNKNOWN; +} +DWORD NS_WINAPI INFO_GetOSMajorVersion () +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx( &versionInfo ); + + return versionInfo.dwMajorVersion; +} +DWORD NS_WINAPI INFO_GetOSMinorVersion () +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx( &versionInfo ); + + return versionInfo.dwMinorVersion; +} +DWORD NS_WINAPI INFO_GetOSServicePack () +{ + OSVERSIONINFO versionInfo; + char * servicePackString = "Service Pack "; + int servicePackStringLng = strlen(servicePackString); + int servicePackNumber = 0; + + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx( &versionInfo ); + if ( strncmp ( versionInfo.szCSDVersion, servicePackString, servicePackStringLng ) == 0 ) + servicePackNumber = atoi ( &versionInfo.szCSDVersion[servicePackStringLng] ); + + return servicePackNumber; +} +void NS_WINAPI OS_GetComputerName (LPTSTR computerName, int nComputerNameLength ) +{ + DWORD computerNameLength = nComputerNameLength; + GetComputerName( computerName, &computerNameLength ); +} + +PROCESSOR_TYPE NS_WINAPI OS_GetProcessor () +{ + SYSTEM_INFO systemInfo; + GetSystemInfo( &systemInfo); + + switch ( systemInfo.wProcessorArchitecture ) { + case PROCESSOR_ARCHITECTURE_INTEL: + return PROCESSOR_I386; + case PROCESSOR_ARCHITECTURE_MIPS: + return PROCESSOR_MIPS; + case PROCESSOR_ARCHITECTURE_ALPHA: + return PROCESSOR_ALPHA; + case PROCESSOR_ARCHITECTURE_PPC: + return PROCESSOR_PPC; + default: + break; + } + return PROCESSOR_UNKNOWN; +} diff --git a/lib/libnt/path.c b/lib/libnt/path.c new file mode 100644 index 00000000..6691bc98 --- /dev/null +++ b/lib/libnt/path.c @@ -0,0 +1,266 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/*********************************************************** + * Path functions - removing ../ from path + **********************************************************/ +#include <windows.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <io.h> /* For _findfirst */ +#include <direct.h> /* For _rmdir */ +#include <errno.h> +#include "nt/ntos.h" + +DWORD NS_WINAPI +PATH_RemoveRelative ( char * path ) +{ + char * src; + char * dst; + + src = path; + dst = path; + while ( *src ) { + if ( *src == '.' ) { + if ( *(src+1) == '.' ) { + /* strip off the "../" */ + src += 2; + + /* back off least significant directory */ + dst--; + if ( ( *dst == '\\' ) || ( *dst == '/' ) ) + *dst--; + while ( dst > path ) { + if ( ( *dst == '\\' ) || ( *dst == '/' ) ) + break; + dst--; + } + } else { + // remove single "." + } + + } else + *dst++ = *src++; + } + *dst = '\0'; + + return TRUE; +} +DWORD NS_WINAPI +PATH_ConvertNtSlashesToUnix( LPCTSTR lpszNtPath, LPSTR lpszUnixPath ) +{ + if ( lpszNtPath == NULL ) + return 0; + + /* create reverse slashes and escape them */ + while ( *lpszNtPath ) { + if ( *lpszNtPath == '\\' ) + *lpszUnixPath = '/'; + else + *lpszUnixPath = *lpszNtPath; + lpszNtPath++; + lpszUnixPath++; + } + *lpszUnixPath = '\0'; + return 0; +} + +static DWORD +PATH_DeleteRecursivelyFoundFile ( char * fullFileName, char * path, char * fileName ) +{ + if ( strcmp ( fileName, "." ) == 0) + return TRUE; + + if ( strcmp ( fileName, ".." ) == 0) + return TRUE; + + strcpy ( fullFileName, path ); + strcat ( fullFileName, "\\" ); + strcat ( fullFileName, fileName ); + return PATH_DeleteRecursively ( fullFileName ); +} + +/* if the path specified is a file name, the file is deleted + * If the path specifies a directory, the directory is deleted + */ +DWORD NS_WINAPI +PATH_DeleteRecursively ( char * path ) +{ + int result; + unsigned short fileStatus; + struct _stat buf; + struct _finddata_t fileFound; + long hFile; + DWORD retStatus = TRUE; + char fullFileName[_MAX_PATH]; + int error; + + /* Check if statistics are valid: */ + result = _stat( path, &buf ); + if( result != 0 ) + return TRUE; // file or directory does not exist + + fileStatus = buf.st_mode & _S_IFMT; + + /* check if regular file */ + if ( fileStatus & _S_IFREG ) { + if ( remove ( path ) == -1 ) { + error = errno; + switch ( error ) { + case ENOENT: + break; + + case EACCES: + break; + + default: + break; + } + + return FALSE; + } + return TRUE; + } + if ( (fileStatus & _S_IFDIR) == 0 ) + return FALSE; + + + /* path contains a directory, delete all files recursively */ + /* Find first .c file in current directory */ + strcpy ( fullFileName, path ); + strcat ( fullFileName, "\\*.*"); + if( (hFile = _findfirst( fullFileName, &fileFound )) != -1L ) { /* directory contain files? */ + if ( !PATH_DeleteRecursivelyFoundFile ( fullFileName, path, fileFound.name ) ) + retStatus = FALSE; + + /* Find the rest of the .c files */ + while( _findnext( hFile, &fileFound ) == 0 ) { + if ( !PATH_DeleteRecursivelyFoundFile ( fullFileName, path, fileFound.name ) ) + retStatus = FALSE; + } + _findclose( hFile ); + } + + /* remove the directory, now that it is empty */ + if ( _rmdir( path ) == -1 ) + retStatus = FALSE; + return retStatus; +} +/* GetNextFileInDirectory - gets next file in the directory + * Set hFile to zero, when you call it. The routine returns the + * next value for hFile. When the routine returns NULL, there is + * no more files + * + */ +DWORD NS_WINAPI +PATH_GetNextFileInDirectory ( long hFile, char * path, char * lpFileName ) +{ + int result; + unsigned short fileStatus; + struct _stat buf; + struct _finddata_t fileFound; + DWORD retStatus = TRUE; + char fullFileName[_MAX_PATH]; + + if ( hFile == 0 ) { + /* Check if statistics are valid: */ + result = _stat( path, &buf ); + if( result != 0 ) + return 0; // file or directory does not exist + + fileStatus = buf.st_mode & _S_IFMT; + if ( (fileStatus & _S_IFDIR) == 0 ) + return 0; + + + /* path contains a directory, delete all files recursively */ + /* Find first .c file in current directory */ + strcpy ( fullFileName, path ); + strcat ( fullFileName, "\\*.*"); + if( (hFile = _findfirst( fullFileName, &fileFound )) == -1L ) + return 0; + if ( ( strcmp ( fileFound.name , "." ) != 0) + && ( strcmp ( fileFound.name , ".." ) != 0) ) { + strcpy ( lpFileName, fileFound.name ); + return hFile; + } + } + + /* Find the rest of the .c files */ + while( _findnext( hFile, &fileFound ) == 0 ) { + if ( ( strcmp ( fileFound.name , "." ) != 0) + && ( strcmp ( fileFound.name , ".." ) != 0) ) { + strcpy ( lpFileName, fileFound.name ); + return hFile; + } + } + + _findclose( hFile ); + return 0; +} +/*---------------------------------------------------------------------------*\ + * + * Function: PATH_GetNextSubDirectory + * + * Purpose: Gets next sub directory in the path + * + * Input: + * hFile: set to zero first time called; use return value for subsequent calls + * path: directory containing sub directories + * lpSubDirectoryName: buffer to store sub directorie name + * lpSubDirectoryPrefix: chars to exactly match begining of directory name + * + * Returns: + * hFile to be used on subsequent call (0, if no more directories) + * + * Comments: +\*---------------------------------------------------------------------------*/ +DWORD NS_WINAPI +PATH_GetNextSubDirectory( long hFile, char * path, char * lpSubDirectoryName, char * lpSubDirectoryPrefix ) +{ + int result; + unsigned short fileStatus; + struct _stat buf; + char * subDirectoryPrefix; + char * p; + char fullFileName[_MAX_PATH]; + BOOL bSubDirectoryFound; + + do { + hFile = PATH_GetNextFileInDirectory ( hFile, path, lpSubDirectoryName ); + if ( hFile == 0 ) + return 0; + + /* Check if file is a directory */ + strcpy ( fullFileName, path ); + strcat ( fullFileName, "\\" ); + strcat ( fullFileName, lpSubDirectoryName ); + result = _stat( fullFileName, &buf ); + if( result == 0 ) { + fileStatus = buf.st_mode & _S_IFMT; + if ( (fileStatus & _S_IFDIR) == _S_IFDIR ) { + + /* check if sub directory matches prefix */ + bSubDirectoryFound = TRUE; + if ( lpSubDirectoryPrefix ) { + p = lpSubDirectoryName; + subDirectoryPrefix = lpSubDirectoryPrefix; + while ( *subDirectoryPrefix ) { + if ( *subDirectoryPrefix++ != *p++ ) { + bSubDirectoryFound = FALSE; + break; + } + } + } + if ( bSubDirectoryFound ) + return hFile; + } + } + } while ( hFile ); + + return 0; // no more sub directories +} + diff --git a/lib/libnt/pmddeml.c b/lib/libnt/pmddeml.c new file mode 100644 index 00000000..cee541a1 --- /dev/null +++ b/lib/libnt/pmddeml.c @@ -0,0 +1,375 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/**************************************************************************** + PROGRAM: pmddeml.c + + PURPOSE: DDEML interface with ProgMan + +****************************************************************************/ + +#include <windows.h> // required for all Windows applications +#include <ddeml.h> // required for DDEML +#include <stdio.h> // required for strcpy and strlen +#include "nt/ntos.h" // specific to this program + +BOOL PMDDEML_SendShellCommand (DWORD idInst, LPSTR lpCommand); + +HDDEDATA CALLBACK PMDDEML_DdeCallback( UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata,// handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); +/**************************************************************************** + FUNCTION: PMDDEML_Open() + + PURPOSE: Open PMDDEML interface + + PARAMETERS: + + RETURNS: + DWORD handle used in subsequent calls +****************************************************************************/ + +DWORD PMDDEML_Open ( void ) +{ + DWORD idInst = 0L; // instance identifier + + // register this app with the DDEML + if (DdeInitialize(&idInst, // receives instance ID + (PFNCALLBACK)PMDDEML_DdeCallback, // address of callback function + APPCMD_CLIENTONLY, // this is a client app + 0L)) // reserved + return 0; + return idInst; +} +/**************************************************************************** + FUNCTION: PMDDEML_Close() + + PURPOSE: Closes PMDDEML interface + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_Close ( DWORD idInst ) +{ + // free all DDEML resources associated with this app + return DdeUninitialize ( idInst ); +} +/**************************************************************************** + FUNCTION: PMDDEML_CreateProgramManagerGroup() + + PURPOSE: Creates a program group + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + LPCTSTR lpszGroupNamee name of group + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_CreateProgramManagerGroup ( DWORD idInst, LPCTSTR lpszGroupName ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszGroupName == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[CreateGroup(%s)]", lpszGroupName ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} +/**************************************************************************** + FUNCTION: PMDDEML_DeleteProgramManagerGroup() + + PURPOSE: Deletes a program group + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + LPCTSTR lpszGroupNamee name of group + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_DeleteProgramManagerGroup ( DWORD idInst, LPCTSTR lpszGroupName ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszGroupName == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[DeleteGroup(%s)]", lpszGroupName ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +BOOL PMDDEML_DeleteProgramCommonManagerGroup ( DWORD idInst, + LPCTSTR lpszGroupName ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszGroupName == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[DeleteGroup(%s,1)]", lpszGroupName ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +/**************************************************************************** + FUNCTION: PMDDEML_ShowProgramManagerGroup() + + PURPOSE: Deletes a program group + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + LPCTSTR lpszGroupNamee name of group + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_ShowProgramManagerGroup ( DWORD idInst, LPCTSTR lpszGroupName ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszGroupName == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[ShowGroup(%s,1)]", lpszGroupName ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +BOOL PMDDEML_ShowProgramManagerCommonGroup ( DWORD idInst, + LPCTSTR lpszGroupName ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszGroupName == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[ShowGroup(%s,1,1)]", lpszGroupName ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +/**************************************************************************** + FUNCTION: PMDDEML_AddIconToProgramManagerGroup() + + PURPOSE: Deletes icon a program group + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + LPCTSTR lpszCmdLine title of icon in group + LPCTSTR lpszTitle title of icon in group + LPCTSTR lpszWorkingDir title of icon in group + BOOL bReplace True, if icon should be replaced + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_AddIconToProgramManagerGroup ( DWORD idInst, LPCTSTR lpszCmdLine, + LPCTSTR lpszTitle, LPCTSTR lpszIconPath, LPCTSTR lpszWorkingDir, BOOL bReplace ) +{ + char szDDEMsg[256]; // instance identifier + + if ( ( lpszCmdLine == NULL ) || ( lpszTitle == NULL ) || ( lpszWorkingDir == NULL ) ) + return FALSE; + + if ( bReplace ) { + sprintf ( szDDEMsg, "[ReplaceItem(%s)]", lpszTitle ); + PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ); + } + + sprintf ( szDDEMsg, "[AddItem(%s,%s,%s,,,,%s)]", lpszCmdLine, lpszTitle, + lpszIconPath, lpszWorkingDir ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +/**************************************************************************** + FUNCTION: PMDDEML_DeleteIconInProgramManagerGroup() + + PURPOSE: Deletes icon a program group + + PARAMETERS: + DWORD idInst handle returned by PMDDEML_Open + LPCTSTR lpszTitle title of icon in group + + RETURNS: + TRUE, if successful +****************************************************************************/ + +BOOL PMDDEML_DeleteIconInProgramManagerGroup ( DWORD idInst, LPCTSTR lpszTitle ) +{ + char szDDEMsg[256]; // instance identifier + + if ( lpszTitle == NULL ) + return FALSE; + + sprintf ( szDDEMsg, "[DeleteItem(%s)]", lpszTitle ); + + if ( !PMDDEML_SendShellCommand(idInst, (LPSTR)szDDEMsg ) ) + return FALSE; + return TRUE; +} + +/**************************************************************************** + FUNCTION: PMDDEML_DdeCallback() + + PURPOSE: Processes messages for DDEML conversation + + PARAMETERS: + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata,// handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + + RETURNS: + HDDEDATA +****************************************************************************/ + +HDDEDATA CALLBACK PMDDEML_DdeCallback( UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata,// handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ) +{ + // Nothing need be done here... + return (HDDEDATA)NULL; +} + + +/**************************************************************************** + FUNCTION: PMDDEML_SendShellCommand() + + PURPOSE: Sends the given command string to Program Manager + + PARAMETERS: + LPSTR - pointer to command string + + RETURNS: + BOOL - TRUE if this function succeeds, FALSE otherwise +****************************************************************************/ + +BOOL PMDDEML_SendShellCommand (DWORD idInst, // instance identifier + LPSTR lpCommand) // command string to execute +{ + HSZ hszServTop; // Service and Topic name are "PROGMAN" + HCONV hconv; // handle of conversation + int nLen; // length of command string + HDDEDATA hData; // return value of DdeClientTransaction + DWORD dwResult; // result of transaction + BOOL bResult=FALSE; // TRUE if this function is successful + + // create string handle to service/topic + hszServTop = DdeCreateStringHandle(idInst, "PROGMAN", CP_WINANSI); + + // attempt to start conversation with server app + if ((hconv = DdeConnect(idInst, hszServTop, hszServTop, NULL))!= NULL) + { + // get length of the command string + nLen = lstrlen((LPSTR)lpCommand); + + // send command to server app + hData = DdeClientTransaction((LPBYTE)lpCommand, // data to pass + nLen + 1, // length of data + hconv, // handle of conversation + NULL, // handle of name-string + CF_TEXT, // clipboard format + XTYP_EXECUTE, // transaction type + 1000, // timeout duration + &dwResult); // points to transaction result + + if (hData) + bResult = TRUE; + + // end conversation + DdeDisconnect(hconv); + } + + // free service/topic string handle + DdeFreeStringHandle(idInst, hszServTop); + + return bResult; +} + + +/**************************************************************************** + FUNCTION: PMDDEML_GetProgramGroupInfo() + + PURPOSE: Gets group info from progman + + PARAMETERS: + LPSTR - pointer to command string + + RETURNS: + BOOL - TRUE if this function succeeds, FALSE otherwise +****************************************************************************/ +BOOL PMDDEML_GetProgramGroupInfo(DWORD idInst, LPSTR lpProgramGroup, char *szBuffer, DWORD cbBuffer) +{ + HSZ hszServTop; // Service and Topic name are "PROGMAN" + HSZ hszTopic; // Topic name is the lpRequest + HCONV hconv; // handle of conversation + HDDEDATA hData = 0; // return value of DdeClientTransaction + DWORD dwResult; // result of transaction + BOOL bResult=FALSE; // TRUE if this function is successful + + hszServTop = DdeCreateStringHandle(idInst, "PROGMAN", CP_WINANSI); + hszTopic = DdeCreateStringHandle(idInst, lpProgramGroup, CP_WINANSI); + + if((hconv = DdeConnect(idInst, hszServTop, hszServTop, NULL)) != NULL) + { + hData = DdeClientTransaction((LPBYTE)NULL, // data to pass + 0L, // length of data + hconv, // handle of conversation + hszTopic, // handle of name-string + CF_TEXT, // clipboard format + XTYP_REQUEST, // transaction type + 5000, // timeout duration + &dwResult); // points to transaction result + + bResult = (BOOL)DdeGetData(hData, (void FAR*)szBuffer, cbBuffer, 0); + DdeDisconnect(hconv); + } + + // free service/topic string handle + DdeFreeStringHandle(idInst, hszServTop); + DdeFreeStringHandle(idInst, hszTopic); + + return bResult; +} diff --git a/lib/libnt/registry.c b/lib/libnt/registry.c new file mode 100644 index 00000000..2e6ec639 --- /dev/null +++ b/lib/libnt/registry.c @@ -0,0 +1,242 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +// ERROR.C +// +// This file contains the functions needed to install the httpd server. +// They are as follows. +// +// getreg.c +// +// This file has the function needed to get a particular value of a key from the registry... +// 1/16/95 aruna +// + +#include <windows.h> +#include "nt/ntos.h" + +BOOL NS_WINAPI +REG_CheckIfKeyExists( HKEY hKey, LPCTSTR key ) +{ + HKEY hQueryKey; + + if (RegOpenKeyEx(hKey, key, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + RegCloseKey(hQueryKey); + return TRUE; +} + +BOOL NS_WINAPI +REG_GetRegistryParameter( + HKEY hKey, + LPCTSTR registryKey, + LPTSTR QueryValueName, + LPDWORD ValueType, + LPBYTE ValueBuffer, + LPDWORD ValueBufferSize + ) +{ + HKEY hQueryKey; + + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + + return FALSE; + } + + if (RegQueryValueEx(hQueryKey, QueryValueName, 0, + ValueType, ValueBuffer, ValueBufferSize) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + + RegCloseKey(hQueryKey); + return TRUE; +} + +BOOL NS_WINAPI +REG_CreateKey( HKEY hKey, LPCTSTR registryKey ) +{ + HKEY hNewKey; + + if ( RegCreateKey (hKey, registryKey, &hNewKey) != ERROR_SUCCESS) { + return FALSE; + } + + RegCloseKey(hNewKey); + return TRUE; +} + +BOOL NS_WINAPI +REG_DeleteKey( HKEY hKey, LPCTSTR registryKey ) +{ + HKEY hQueryKey; + DWORD dwNumberOfSubKeys; + char registrySubKey[256]; + DWORD i; + + /* if key does not exist, then consider it deleted */ + if ( !REG_CheckIfKeyExists( hKey, registryKey ) ) + return TRUE; + + if ( !REG_GetSubKeysInfo( hKey, registryKey, &dwNumberOfSubKeys, NULL ) ) + return FALSE; + + if ( dwNumberOfSubKeys ) { + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + // loop through all sub keys and delete the subkeys (recursion) + for ( i=0; i<dwNumberOfSubKeys; i++ ) { + if ( RegEnumKey( hQueryKey, 0, registrySubKey, sizeof(registrySubKey) ) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + if ( !REG_DeleteKey( hQueryKey, registrySubKey ) ) { + RegCloseKey(hQueryKey); + return FALSE; + } + } + RegCloseKey(hQueryKey); + } + + if ( RegDeleteKey (hKey, registryKey) != ERROR_SUCCESS) { + return FALSE; + } + + return TRUE; +} + +BOOL NS_WINAPI +REG_GetSubKey( HKEY hKey, LPCTSTR registryKey, DWORD nSubKeyIndex, LPTSTR registrySubKeyBuffer, DWORD subKeyBufferSize ) +{ + HKEY hQueryKey; + + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + if ( RegEnumKey( hQueryKey, nSubKeyIndex, registrySubKeyBuffer, subKeyBufferSize ) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + + RegCloseKey(hQueryKey); + return TRUE; +} + +BOOL NS_WINAPI +REG_GetSubKeysInfo( HKEY hKey, LPCTSTR registryKey, LPDWORD lpdwNumberOfSubKeys, LPDWORD lpdwMaxSubKeyLength ) +{ + HKEY hQueryKey; + char szClass[256]; // address of buffer for class string + DWORD cchClass; // address of size of class string buffer + DWORD cSubKeys; // address of buffer for number of subkeys + DWORD cchMaxSubkey; // address of buffer for longest subkey name length + DWORD cchMaxClass; // address of buffer for longest class string length + DWORD cValues; // address of buffer for number of value entries + DWORD cchMaxValueName; // address of buffer for longest value name length + DWORD cbMaxValueData; // address of buffer for longest value data length + DWORD cbSecurityDescriptor; // address of buffer for security descriptor length + FILETIME ftLastWriteTime; // address of buffer for last write time + + + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + if ( RegQueryInfoKey( hQueryKey, // handle of key to query + (char*)&szClass, // address of buffer for class string + &cchClass, // address of size of class string buffer + NULL, // reserved + &cSubKeys, // address of buffer for number of subkeys + &cchMaxSubkey, // address of buffer for longest subkey name length + &cchMaxClass, // address of buffer for longest class string length + &cValues, // address of buffer for number of value entries + &cchMaxValueName, // address of buffer for longest value name length + &cbMaxValueData, // address of buffer for longest value data length + &cbSecurityDescriptor, // address of buffer for security descriptor length + &ftLastWriteTime // address of buffer for last write time + ) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + + // return desired information + if ( lpdwNumberOfSubKeys ) + *lpdwNumberOfSubKeys = cSubKeys; + if ( lpdwMaxSubKeyLength ) + *lpdwMaxSubKeyLength = cchMaxSubkey; + + RegCloseKey(hQueryKey); + return TRUE; +} + +BOOL NS_WINAPI +REG_SetRegistryParameter( + HKEY hKey, + LPCTSTR registryKey, + LPTSTR valueName, + DWORD valueType, + LPCTSTR ValueString, + DWORD valueStringLength + ) +{ + HKEY hQueryKey; + + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + if ( RegSetValueEx( hQueryKey, valueName, 0, valueType, (CONST BYTE *)ValueString, + valueStringLength ) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + + RegCloseKey(hQueryKey); + return TRUE; +} + + + + +BOOL NS_WINAPI +REG_DeleteValue( HKEY hKey, LPCTSTR registryKey, LPCSTR valueName ) +{ + HKEY hQueryKey; + DWORD ValueBufferSize = 256; + char ValueBuffer[256]; + DWORD i, ValueType; + + /* if key does not exist, then consider it deleted */ + if (RegOpenKeyEx(hKey, registryKey, 0, KEY_ALL_ACCESS, + &hQueryKey) != ERROR_SUCCESS) { + return FALSE; + } + + /* if valuename does not exist, then consider it deleted */ + if (RegQueryValueEx(hQueryKey, valueName, 0, + &ValueType, ValueBuffer, &ValueBufferSize) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return TRUE; + } + + if (RegDeleteValue(hQueryKey, valueName) != ERROR_SUCCESS) { + RegCloseKey(hQueryKey); + return FALSE; + } + + RegCloseKey(hQueryKey); + return TRUE; +} diff --git a/lib/libnt/service.c b/lib/libnt/service.c new file mode 100644 index 00000000..1bfc5a5e --- /dev/null +++ b/lib/libnt/service.c @@ -0,0 +1,375 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <windows.h> +#include "nt/ntos.h" + +#define SERVRET_ERROR 0 +#define SERVRET_INSTALLED 1 +#define SERVRET_STARTING 2 +#define SERVRET_STARTED 3 +#define SERVRET_STOPPING 4 +#define SERVRET_REMOVED 5 + + +DWORD NS_WINAPI +SERVICE_GetNTServiceStatus(LPCTSTR szServiceName, LPDWORD lpLastError ) +{ + SERVICE_STATUS ServiceStatus; + SC_HANDLE schService = NULL; + SC_HANDLE schSCManager = NULL; + DWORD lastError = 0; + int ret = 0; + + //ereport(LOG_INFORM, "open SC Manager"); + if ((schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + )) == NULL ) { + lastError = GetLastError(); + ret = SERVRET_ERROR; + goto finish; + } + + schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS); + + if (schService == NULL ) { + lastError = GetLastError(); + if (lastError == ERROR_SERVICE_DOES_NOT_EXIST) { + lastError = 0; + ret = SERVRET_REMOVED; + } else + ret = SERVRET_ERROR; + goto finish; + } + + ret = ControlService(schService, SERVICE_CONTROL_INTERROGATE, &ServiceStatus); + + if ( !ret ) { + lastError = GetLastError(); + if ( lastError == ERROR_SERVICE_NOT_ACTIVE ) { + lastError = 0; + ret = SERVRET_INSTALLED; + } else + ret = SERVRET_ERROR; + goto finish; + } + + switch ( ServiceStatus.dwCurrentState ) { + case SERVICE_STOPPED: ret = SERVRET_INSTALLED; break; + case SERVICE_START_PENDING: ret = SERVRET_STARTING; break; + case SERVICE_STOP_PENDING: ret = SERVRET_STOPPING; break; + case SERVICE_RUNNING: ret = SERVRET_STARTED; break; + case SERVICE_CONTINUE_PENDING: ret = SERVRET_STARTED; break; + case SERVICE_PAUSE_PENDING: ret = SERVRET_STARTED; break; + case SERVICE_PAUSED: ret = SERVRET_STARTED; break; + default: ret = SERVRET_ERROR; break; + } + +finish: + if ( schService) + CloseServiceHandle(schService); + if ( schSCManager) + CloseServiceHandle(schSCManager); + if ( lpLastError ) + *lpLastError = lastError; + return ret; +} + +DWORD NS_WINAPI +SERVICE_InstallNTService(LPCTSTR szServiceName, LPCTSTR szServiceDisplayName, LPCTSTR szServiceExe ) +{ + LPCTSTR lpszBinaryPathName = szServiceExe; + SC_HANDLE schService = NULL; + SC_HANDLE schSCManager = NULL; + int lastError = 0; + int ret = 0; + + + //ereport(LOG_INFORM, "open SC Manager"); + if ((schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + )) == NULL ) { + lastError = GetLastError(); + goto finish; + } + + /* check if service already exists */ + schService = OpenService( schSCManager, + szServiceName, + SERVICE_ALL_ACCESS + ); + if (schService) { + lastError = ERROR_SERVICE_EXISTS; + goto finish; + } + + schService = CreateService( + schSCManager, // SCManager database + szServiceName, // name of service + szServiceDisplayName, // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS, // service type + SERVICE_AUTO_START, //SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + lpszBinaryPathName, // service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService == NULL) { + lastError = GetLastError(); + } + + // successfully installed service + +finish: + if ( schService) + CloseServiceHandle(schService); + if ( schSCManager) + CloseServiceHandle(schSCManager); + return lastError; +} + + +DWORD NS_WINAPI +SERVICE_RemoveNTService(LPCTSTR szServiceName) +{ + SC_HANDLE schService = NULL; + SC_HANDLE schSCManager = NULL; + int lastError = 0; + int ret = 0; + + //ereport(LOG_INFORM, "open SC Manager"); + if ((schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + )) == NULL ) { + lastError = GetLastError(); + goto finish; + } + + schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS); + + if (schService == NULL ) { + lastError = GetLastError(); + goto finish; + } + + ret = DeleteService(schService); + + if ( !ret) { + lastError = GetLastError(); + goto finish; + } + + // successfully removed service + +finish: + if ( schService) + CloseServiceHandle(schService); + if ( schSCManager) + CloseServiceHandle(schSCManager); + return lastError; +} + +DWORD NS_WINAPI +SERVICE_StartNTService(LPCTSTR szServiceName) +{ + SC_HANDLE schService = NULL; + SC_HANDLE schSCManager = NULL; + int lastError = 0; + int ret = 0; + + //ereport(LOG_INFORM, "open SC Manager"); + if ((schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + )) == NULL ) { + lastError = GetLastError(); + goto finish; + } + + schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS); + + if (schService == NULL ) { + lastError = GetLastError(); + goto finish; + } + + ret = StartService(schService, 0, NULL); + + if ( !ret ) { + lastError = GetLastError(); + goto finish; + } + + // successfully started service + + +finish: + if ( schService) + CloseServiceHandle(schService); + if ( schSCManager) + CloseServiceHandle(schSCManager); + return lastError; +} + +DWORD NS_WINAPI +SERVICE_StartNTServiceAndWait(LPCTSTR szServiceName, LPDWORD lpdwLastError) +{ + DWORD dwLastError; + DWORD dwStatus; + int i; + + /* check if service is running */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_STARTED ) + return TRUE; + + dwLastError = SERVICE_StartNTService( szServiceName ); + if ( dwLastError != 0 ) { + goto errorExit; + } + + for ( i=0; i<5; i++ ) { + // make sure the service got installed + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_ERROR) { + if ( dwLastError != ERROR_SERVICE_CANNOT_ACCEPT_CTRL ) + goto errorExit; + } else if ( dwStatus == SERVRET_STARTED ) + return TRUE; + + Sleep ( 1000 ); + } + + dwLastError = 0; + +errorExit: + if ( lpdwLastError ) + *lpdwLastError = dwLastError; + return FALSE; +} + +DWORD NS_WINAPI +SERVICE_StopNTService(LPCTSTR szServiceName) +{ + SC_HANDLE schService = NULL; + SC_HANDLE schSCManager = NULL; + int lastError = 0; + int ret = 0; + SERVICE_STATUS ServiceStatus; + + //ereport(LOG_INFORM, "open SC Manager"); + if ((schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + )) == NULL ) { + lastError = GetLastError(); + goto finish; + } + + schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS); + + if (schService == NULL ) { + lastError = GetLastError(); + goto finish; + } + + ret = ControlService(schService, SERVICE_CONTROL_STOP, &ServiceStatus); + + if ( !ret ) { + lastError = GetLastError(); + goto finish; + } + + // server is stopping + +finish: + if ( schService) + CloseServiceHandle(schService); + if ( schSCManager) + CloseServiceHandle(schSCManager); + return lastError; +} + +DWORD NS_WINAPI +SERVICE_StopNTServiceAndWait(LPCTSTR szServiceName, LPDWORD lpdwLastError) +{ + DWORD dwLastError; + DWORD dwStatus; + int i; + + /* check if service is running */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus != SERVRET_STARTED ) + return TRUE; + + for ( i=0; i<30; i++ ) { + dwLastError = SERVICE_StopNTService( szServiceName ); + Sleep ( 1000 ); + + // make sure the service is stoppped and just installed + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + Sleep ( 1000 ); + if ( dwStatus == SERVRET_INSTALLED ) { + Sleep ( 1000 ); + return TRUE; + } + } + + if ( lpdwLastError ) + *lpdwLastError = dwLastError; + return FALSE; +} + +DWORD NS_WINAPI +SERVICE_ReinstallNTService(LPCTSTR szServiceName, LPCTSTR szServiceDisplayName, LPCTSTR szServiceExe ) +{ + DWORD dwLastError; + DWORD dwStatus; + int i; + + for ( i=0; i< 5; i++ ) { + + /* if service is running, stop it */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_STARTED ) + SERVICE_StopNTServiceAndWait( szServiceName, &dwLastError ); + + /* if service is installed, remove it */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_INSTALLED ) + SERVICE_RemoveNTService( szServiceName ); + + /* try and install the service again */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_REMOVED ) + SERVICE_InstallNTService( szServiceName, szServiceDisplayName, szServiceExe ); + + /* try and start the service again */ + dwStatus = SERVICE_GetNTServiceStatus( szServiceName, &dwLastError ); + if ( dwStatus == SERVRET_INSTALLED ) { + return NO_ERROR; + } + } + + /* if no error reported, force an error */ + if ( dwLastError == NO_ERROR ) + dwLastError = (DWORD)-1; + + return dwLastError; +} + diff --git a/lib/libnt/tcpip.c b/lib/libnt/tcpip.c new file mode 100644 index 00000000..e15ccdba --- /dev/null +++ b/lib/libnt/tcpip.c @@ -0,0 +1,145 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <windows.h> +#include "nt/ntos.h" + +/*---------------------------------------------------------------------------*\ + * + * Function: GetServerDefaultHostName + * + * Purpose: This function gets the default host name + * + * Input: + * + * Returns: + * + * Comments: +\*---------------------------------------------------------------------------*/ +DWORD NS_WINAPI +TCPIP_GetDefaultHostName( LPTSTR lpszFullHostName, LPTSTR lpszHostName, LPTSTR lpszDomainName ) +{ + char * szKey; + char * szName; + DWORD dwValueType; + DWORD dwIpHostSize = 256; + char szIpHost[256]; + DWORD dwIpDomainSize = 256; + char szIpDomain[256]; + BOOL bWinNT; + + /* get operating system */ + switch ( INFO_GetOperatingSystem() ) { + case OS_WIN95: bWinNT = FALSE; break; + case OS_WINNT: bWinNT = TRUE; break; + default: return TCPIP_UNSUPPORTED_OS; + } + + +#if 0 + int lastError; + WSADATA WSAData; + if ( WSAStartup( 0x0101, &WSAData ) != 0 ) { + lastError = WSAGetLastError(); + m_pMainWnd->MessageBox ( "TCP/IP must be installed.\nUse the Network Icon in Control Panel" ); + return FALSE; + } + lastError = gethostname ( szIpHost, sizeof(szIpHost) ); +#endif + + /* get list of all keys under Netscape */ + if ( bWinNT ) + szKey = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + else + szKey = "SYSTEM\\CurrentControlSet\\Services\\Vxd\\MSTCP"; + + if( !REG_CheckIfKeyExists( HKEY_LOCAL_MACHINE, szKey ) ) { + return TCPIP_NO_TCPIP; + } + + /* get host name for computer. May have to get DHCP host name if empty */ + szName = "Hostname"; + if( !REG_GetRegistryParameter( HKEY_LOCAL_MACHINE, szKey, szName, &dwValueType, (LPBYTE)szIpHost, &dwIpHostSize ) ) { + szIpHost[0] = '\0'; + } + + /* get domain name for computer. May have to get DHCP host name if empty */ + szName = "Domain"; + if( !REG_GetRegistryParameter( HKEY_LOCAL_MACHINE, szKey, szName, &dwValueType, (LPBYTE)szIpDomain, &dwIpDomainSize ) ) { + dwIpDomainSize = 0; + } + if ( dwIpDomainSize == 0 ) { + szName = "DhcpDomain"; + if( !REG_GetRegistryParameter( HKEY_LOCAL_MACHINE, szKey, szName, &dwValueType, (LPBYTE)szIpDomain, &dwIpDomainSize ) ) { + dwIpDomainSize = 0; + } + } + + if ( lpszHostName ) + strcpy ( lpszHostName, szIpHost ); + + strcpy ( lpszFullHostName, szIpHost ); + if ( lpszDomainName ) { + if ( dwIpDomainSize == 0 ) + *lpszDomainName = '\0'; + else { + strcpy ( lpszDomainName, szIpDomain ); + strcat ( lpszFullHostName, "." ); + strcat ( lpszFullHostName, lpszDomainName ); + } + } + + return TCPIP_NO_ERROR; +} +/*---------------------------------------------------------------------------*\ + * + * Function: TCPIP_VerifyHostName + * + * Purpose: This function validates the host name + * + * Input: + * + * Returns: + * + * Comments: +\*---------------------------------------------------------------------------*/ +DWORD NS_WINAPI +TCPIP_VerifyHostName( LPCTSTR lpszHostName ) +{ + struct hostent *ent; + WSADATA wsd; + int lastError; + + if(WSAStartup(MAKEWORD(1, 1), &wsd) != 0) + return TCPIP_NO_WINSOCK_DLL; + + ent = gethostbyname ( lpszHostName ); + lastError = WSAGetLastError(); + WSACleanup(); + + if ( ent == NULL ) { + switch ( lastError ) { + case WSANOTINITIALISED: // A successful WSAStartup must occur before using this function. + break; + case WSAENETDOWN: // The Windows Sockets implementation has detected that the network subsystem has failed. + return TCPIP_NETWORK_DOWN; + case WSAHOST_NOT_FOUND: // Authoritative Answer Host not found. + return TCPIP_HOST_NOT_FOUND; + case WSATRY_AGAIN: // Non-Authoritative Host not found, or SERVERFAIL. + return TCPIP_HOST_SERVER_DOWN; + case WSANO_RECOVERY: // Nonrecoverable errors: FORMERR, REFUSED, NOTIMP. + return TCPIP_NETWORK_ERROR; + case WSANO_DATA: // Valid name, no data record of requested type. + return TCPIP_HOST_VALID_NAME; + case WSAEINPROGRESS: // A blocking Windows Sockets operation is in progress. + return TCPIP_NETWORK_ERROR; + case WSAEINTR: // The (blocking) call was canceled using + return TCPIP_NETWORK_ERROR; + default: + return TCPIP_NETWORK_ERROR; + } + } + return TCPIP_NO_ERROR; +} diff --git a/lib/libsi18n/Makefile b/lib/libsi18n/Makefile new file mode 100644 index 00000000..9b1c9c2e --- /dev/null +++ b/lib/libsi18n/Makefile @@ -0,0 +1,155 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# The libsi18n library support for getting strings from the +# string database. + +MCOM_ROOT=../../.. +MODULE=LibsI18N +include ../../nsdefs.mk + +NSDEFS_PRODUCT = $(NS_PRODUCT) + +OBJDEST=$(OBJDIR)/lib/libsi18n + +L10NDIR = $(MCOM_ROOT)/ldapserver/l10n + +ifeq ($(ARCH), WINNT) +CCOUT0= /Fo +CCOUT1= /Fe +LIBS=$(OBJDIR)/lib/libsi18n.lib +ifeq ($(BSCINFO), yes) +BSCS=$(OBJDIR)/lib/libsi18n.bsc +endif +else +LIBS=$(OBJDIR)/lib/libsi18n.a +CCOUT0= -o +CCOUT1= -o +endif + +ifeq ($(ARCH), IRIX) + ifeq ($(USE_N32), 1) + MYLDFLAG = -n32 + else + MYLDFLAG = + endif +else + MYLDFLAG = +endif + +ifeq ($(USE_64), 1) +ifeq ($(ARCH), HPUX) + MYLDFLAG = +DA2.0W +endif +ifeq ($(ARCH), SOLARIS) + MYLDFLAG = -xarch=v9 +endif +endif +ifeq ($(BUILD_MODULE), HTTP_ENTERPRISE) +StringDatabase = ns-httpd.db +L10N_SERVER = httpd +DBTheaders = \ + ../../httpd/src/dbthttpd.h \ + ../../include/base/dbtbase.h \ + ../../include/frame/dbtframe.h \ + ../../include/httpdaemon/dbthttpdaemon.h \ + ../../include/libaccess/dbtlibaccess.h \ + ../../include/libadmin/dbtlibadmin.h \ + ../../include/libir/dbtlibir.h \ + gshttpd.h +endif + +ifeq ($(BUILD_MODULE), DIRECTORY) + StringDatabase = ns-slapd.properties + DBTheaders = \ + ../../httpd/src/dbthttpd.h \ + ../../include/base/dbtbase.h \ + ../../include/frame/dbtframe.h \ + ../../include/httpdaemon/dbthttpdaemon.h \ + ../../include/libaccess/dbtlibaccess.h \ + ../../include/libadmin/dbtlibadmin.h \ + ../../include/libir/dbtlibir.h \ + ../../ldap/clients/dsgw/dbtdsgw.h \ + gsslapd.h + endif + + +ifeq ($(BUILD_MODULE), HTTP_PERSONAL) +StringDatabase = ns-httpd.db +L10N_SERVER = httpd +DBTheaders = \ + ../../httpd/src/dbthttpd.h \ + ../../include/base/dbtbase.h \ + ../../include/frame/dbtframe.h \ + ../../include/httpdaemon/dbthttpdaemon.h \ + ../../include/libaccess/dbtlibaccess.h \ + ../../include/libadmin/dbtlibadmin.h \ + gshttpd.h +endif + +ifeq ($(BUILD_MODULE), HTTP_PROXY) +StringDatabase = ns-proxy.db +DBTheaders = \ + ../../httpd/src/dbthttpd.h \ + ../../include/base/dbtbase.h \ + ../../include/frame/dbtframe.h \ + ../../include/httpdaemon/dbthttpdaemon.h \ + ../../include/libaccess/dbtlibaccess.h \ + ../../include/libadmin/dbtlibadmin.h \ + gsproxy.h +endif + +ifeq ($(BUILD_MODULE), HTTP_ADMIN) +StringDatabase = ns-admin.db +L10N_SERVER = admserv +DBTheaders = \ + ../../admserv/src/dbtadmserv.h \ + ../../admserv/cgi-src/dbtcgiadmin.h \ + ../../admserv/user-forms/src/dbtuserforms.h \ + ../../include/base/dbtbase.h \ + ../../include/frame/dbtframe.h \ + ../../include/httpdaemon/dbthttpdaemon.h \ + ../../include/libaccess/dbtlibaccess.h \ + ../../include/libadmin/dbtlibadmin.h \ + ../../include/libir/dbtlibir.h \ + gsadmserv.h +endif + +include ../../nsconfig.mk + +MCC_INCLUDE += $(ADMINUTIL_INCLUDE) + +all: $(OBJDEST) $(LIBS) $(BSCS) $(OBJDEST)/$(StringDatabase) + +$(OBJDEST)/$(StringDatabase): makstrdb.c ../../include/i18n.h $(DBTheaders) + @echo "Building String Database for $(BUILD_MODULE) in" + @echo "$(OBJDEST)/$(StringDatabase)" + $(CC) -g $(CFLAGS) $(MCC_INCLUDE) -c makstrdb.c \ + $(CCOUT0)$(OBJDEST)/makstrdb.o + $(CC) $(MYLDFLAG) -g $(OBJDEST)/makstrdb.o \ + $(CCOUT1)$(OBJDEST)/makstrdb.exe + cd $(OBJDEST); ./makstrdb.exe + rm $(OBJDEST)/makstrdb.exe + +$(OBJDEST): + mkdir -p $(OBJDEST) + +OBJS = \ + $(OBJDEST)/acclanglist.o \ + $(OBJDEST)/getstrprop.o \ + $(OBJDEST)/getlang.o \ + $(OBJDEST)/coreres.o \ + $(OBJDEST)/txtfile.o \ + $(OBJDEST)/propset.o \ + $(OBJDEST)/reshash.o + + + +$(LIBS): $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ diff --git a/lib/libsi18n/acclanglist.c b/lib/libsi18n/acclanglist.c new file mode 100644 index 00000000..cc985cde --- /dev/null +++ b/lib/libsi18n/acclanglist.c @@ -0,0 +1,187 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#include "i18n.h" + + +/* + * Accept-Language = "Accept-Language" ":" + * 1#( language-range [ ";" "q" "=" qvalue ] ) + * language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + * + * NLS_AccLangList() assumes that "Accept-Language:" has already + * been stripped off. It takes as input + * + * 1#( ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) [ ";" "q" "=" qvalue ] ) + * + * and returns a list of languages, ordered by qvalues, in + * the array NLS_ACCEPT_LANGUAGE_LIST. + * + * If there are to many languages (>NLS_MAX_ACCEPT_LANGUAGE) the excess + * is ignored. If the language-range is too long (>NLS_MAX_ACCEPT_LENGTH), + * the language-range is ignored. In these cases, NLS_AccLangList() + * will quietly return, perhaps with numLang = 0. numLang is + * returned by the function. + */ + + +static size_t +AcceptLangList(const char* AcceptLanguage, + ACCEPT_LANGUAGE_LIST AcceptLanguageList) +{ + char* input; + char* cPtr; + char* cPtr1; + char* cPtr2; + int i; + int j; + int countLang = 0; + + input = strdup(AcceptLanguage); + if (input == (char*)NULL){ + return 0; + } + + cPtr1 = input-1; + cPtr2 = input; + + /* put in standard form */ + while (*(++cPtr1)) { + if (isalpha(*cPtr1)) *cPtr2++ = tolower(*cPtr1); /* force lower case */ + else if (isspace(*cPtr1)); /* ignore any space */ + else if (*cPtr1=='-') *cPtr2++ = '_'; /* "-" -> "_" */ + else if (*cPtr1=='*'); /* ignore "*" */ + else *cPtr2++ = *cPtr1; /* else unchanged */ + } + *cPtr2 = '\0'; + + countLang = 0; + + if (strchr(input,';')) { + /* deal with the quality values */ + + float qvalue[MAX_ACCEPT_LANGUAGE]; + float qSwap; + float bias = 0.0f; + char* ptrLanguage[MAX_ACCEPT_LANGUAGE]; + char* ptrSwap; + + cPtr = strtok(input,","); + while (cPtr) { + qvalue[countLang] = 1.0f; + if (cPtr1 = strchr(cPtr,';')) { + sscanf(cPtr1,";q=%f",&qvalue[countLang]); + *cPtr1 = '\0'; + } + if (strlen(cPtr)<MAX_ACCEPT_LENGTH) { /* ignore if too long */ + qvalue[countLang] -= (bias += 0.0001f); /* to insure original order */ + ptrLanguage[countLang++] = cPtr; + if (countLang>=MAX_ACCEPT_LANGUAGE) break; /* quit if too many */ + } + cPtr = strtok(NULL,","); + } + + /* sort according to decending qvalue */ + /* not a very good algorithm, but count is not likely large */ + for ( i=0 ; i<countLang-1 ; i++ ) { + for ( j=i+1 ; j<countLang ; j++ ) { + if (qvalue[i]<qvalue[j]) { + qSwap = qvalue[i]; + qvalue[i] = qvalue[j]; + qvalue[j] = qSwap; + ptrSwap = ptrLanguage[i]; + ptrLanguage[i] = ptrLanguage[j]; + ptrLanguage[j] = ptrSwap; + } + } + } + for ( i=0 ; i<countLang ; i++ ) { + strcpy(AcceptLanguageList[i],ptrLanguage[i]); + } + + } else { + /* simple case: no quality values */ + + cPtr = strtok(input,","); + while (cPtr) { + if (strlen(cPtr)<MAX_ACCEPT_LENGTH) { /* ignore if too long */ + strcpy(AcceptLanguageList[countLang++],cPtr); + if (countLang>=MAX_ACCEPT_LANGUAGE) break; /* quit if too many */ + } + cPtr = strtok(NULL,","); + } + } + + free(input); + + return countLang; +} + +/* + * Get prioritized locale list from NLS_AcceptLangList + * + * Add additonal language to the list for fallback if locale + * name is language_region + * + */ + + +int +XP_AccLangList(char* AcceptLanguage, + ACCEPT_LANGUAGE_LIST AcceptLanguageList) +{ + int i; + int n; + char *defaultLanguage = "en"; + ACCEPT_LANGUAGE_LIST curLanguageList; + int index = 0; + char lang[3]; + int k; + + n = AcceptLangList(AcceptLanguage, curLanguageList); + + if (n == 0) + return 0; + + memset(lang, 0, 3); + for (i = 0; i < n; i++) { + if (*lang && (strncmp(lang, curLanguageList[i], 2) != 0)) { + /* add lang if current language is the last occurence in the list */ + for (k = i+1; (k < n) && strncmp(curLanguageList[k],lang,2); k++); + + if (k == n) { + strcpy(AcceptLanguageList[index++], lang); + *lang = '\0'; + } + } + + strcpy(AcceptLanguageList[index++], curLanguageList[i]); + + /* Add current language for future appending.,make sure it's not on list */ + if ((strlen(curLanguageList[i]) > 2) && (curLanguageList[i][2] == '_')) { + strncpy(lang, curLanguageList[i], 2); + for (k = 0; (k < index) && strcmp(AcceptLanguageList[k], lang); k++); + + if (k != index) lang[0] = '\0'; + } + } + + if (lang[0] != '\0') + strcpy(AcceptLanguageList[index++], lang); /* add new lang */ + + /* Append defaultLanguage if it's not in the list */ + for (i = 0; (i < index) && strcmp(AcceptLanguageList[i], defaultLanguage); i++); + + if (i == index) + strcpy(AcceptLanguageList[index++], defaultLanguage); + + return index; +} diff --git a/lib/libsi18n/coreres.c b/lib/libsi18n/coreres.c new file mode 100644 index 00000000..5e438069 --- /dev/null +++ b/lib/libsi18n/coreres.c @@ -0,0 +1,113 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include "i18n.h" + +#include "libadminutil/resource.h" +#include "propset.h" + +#include "coreres.h" +#if 0 +typedef struct ResourceS +{ + char *path; + char *package; + PropertiesSet *propset; +} Resource; +#endif + +Resource* core_res_init_resource(const char* path, const char* package) +{ + PropertiesSet *propset; + char *directory; + char *filename; + char *file_path; + char *p, *q; + char *filep; + Resource *hres; + + /********************* + Create full path information + eg. ./es40/admin and cgi.bin.start ==> + ./es40/admin/cgi/bin/start.properties + **********************/ + file_path = (char *) malloc (strlen(path) + strlen(package) + 20); + + + strcpy(file_path, path); + if (path[strlen(path)-1] != '/') + strcat(file_path, "/"); + + p = file_path + strlen(file_path); + q = (char *) package; + + filep = p - 1; + + /* Append package to file_path + p: end positon of path + 1 + q: start position of package + */ + while (q && *q) { + if (*q == '.') { + filep = q; + *p ++ = '/'; + } + else + *p ++ = *q ++; + + } + *p = '\0'; + + *filep = '\0'; + filename = filep + 1; + directory = file_path; + + propset = PropertiesInit (directory, filename); + + if (propset == NULL) + return NULL; + + hres = (Resource *) malloc(sizeof(Resource)); + memset(hres, 0, sizeof(Resource)); + + hres->path = strdup(file_path); + hres->propset = propset; + + if (file_path) + free (file_path); + + return hres; +} + +const char *core_res_getstring(Resource *hres, char *key, ACCEPT_LANGUAGE_LIST lang) +{ + + if (key == NULL) + return NULL; + + if (hres) { + return PropertiesGetString(hres->propset, key, lang); + } + + return NULL; +} + +void core_res_destroy_resource(Resource *hres) +{ + if (hres) { + if (hres->path) + free(hres->path); + if (hres->package) + free(hres->package); + PropertiesDestroy(hres->propset); + + free(hres); + } +} + diff --git a/lib/libsi18n/coreres.h b/lib/libsi18n/coreres.h new file mode 100644 index 00000000..9d27060a --- /dev/null +++ b/lib/libsi18n/coreres.h @@ -0,0 +1,15 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef CORERES_H +#define CORERES_H + +#include "libadminutil/resource.h" + +Resource* core_res_init_resource(const char* path, const char* package); +const char *core_res_getstring(Resource *hres, char *key, ACCEPT_LANGUAGE_LIST lang); +void core_res_destroy_resource(Resource *hres); + +#endif diff --git a/lib/libsi18n/getlang.c b/lib/libsi18n/getlang.c new file mode 100644 index 00000000..5aa24d04 --- /dev/null +++ b/lib/libsi18n/getlang.c @@ -0,0 +1,294 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "libadmin/libadmin.h" +#include "frame/conf.h" /* get access on global language setting */ + + +#include "i18n.h" + +/********************************************************************* + strReplace replaces the first instance of from in target with to. + from can be "": to is inserted at start of target. + to can be "": from is removed from target. + if from is not found, 0 is returned; else 1 is returned. + *********************************************************************/ + +static int +strReplace(char* target,char* from,char* to) +{ + /* replace /from/to/ in target */ + + char* pFrom; + char* pOldTail; + int lenTo; + + pFrom = strstr(target,from); + if (pFrom) { + pOldTail = pFrom+strlen(from); + lenTo = strlen(to); + memmove(pFrom+lenTo,pOldTail,strlen(pOldTail)+1); + memcpy(pFrom,to,lenTo); + return 1; + } + + return 0; +} + +/********************************************************************* + statFileDir is a wrapper to stat() that strips trailing slashes + because stat() on Windows seems to return -1 otherwise. +*********************************************************************/ + +int +statFileDir(const char *path, struct stat *info) { + int ret, pathlen; + char *newpath = strdup(path); + + if(newpath == NULL) + return -1; + + for (pathlen = (strlen(newpath) - 1); pathlen >= 0; pathlen--) { + if (newpath[pathlen] == '/' || newpath[pathlen] == '\\') { + newpath[pathlen] = '\0'; + } else { + break; + } + } + + ret = stat(newpath, info); + + if (newpath) + free(newpath); + + return ret; +} + +/********************************************************************* + GetLanguage is reserved for future use. These APIs are not belong + to this file. It needs to be moved to somewhere which knows what's + the current language setting. + *********************************************************************/ + +static char emptyString[] = ""; + +static char client_language[128] = "en"; +static char admin_language[128] = "en"; +static char default_language[128] = "en"; + +void +SetLanguage(int type, char *language) +{ + switch(type) { + case CLIENT_LANGUAGE: + if (language) + strcpy(client_language, language); + break; + case ADMIN_LANGUAGE: + if (language) + strcpy(admin_language, language); + break; + case DEFAULT_LANGUAGE: + if (language) + strcpy(default_language, language); + break; + } + return ; +} + + + +char* +GetClientLanguage(void) +{ + if (client_language) + return client_language; + else + return emptyString; +} + +char* +GetAdminLanguage(void) +{ + if (admin_language) + return admin_language; + else + return emptyString; +} + +char* +GetDefaultLanguage(void) +{ + if (default_language) + return default_language; + else + return "en"; +} + +/********************************************************************* + GetFileForLanguage looks for a file in the appropriate language. + *********************************************************************/ + +NSAPI_PUBLIC +int +GetFileForLanguage(char* filePath,char* language,char* existingFilePath) +{ + /* Input: filePath,language + * filePath is of the form "/xxx/xxx/$$LANGDIR/xxx/xxx/filename" + * or of the form "/xxx/xxx/xxx/xxx/filename". + * filename may or may not have an extension. + * language is an Accept-Language list; each language-range will be + * tried as a subdirectory name and possibly as a filename modifier. + * "*" is ignored - default always provided if needed. + * "-" is replaced by "_". + * $$LANGDIR is a special string replaced by language. It is optional. + * For the default case, $$LANGDIR/ is replaced by nothing + * (so // is not created). + * + * Returned: existingPath + * existingFilePath is the path of a satisfactory, existing file. + * if no file is found, an empty string "" is returned. + * + * int returned: -1 if no file found (existingFilePath = "") + * 0 if default file is returned + * 1 if language file is returned (any in list) + * + * Example: + * filePath = "/path/$$LANGDIR/filename.ext" + * language = "language" + * GetDefaultLanguage() --> "default" + * LANG_DELIMIT = "_" + * + * 1. Try: "/path/language/filename.ext" + * 2. Try: "/path/filename_language.ext" + * 3. Try: "/path/default/filename.ext" + * 4. Try: "/path/filename_default.ext" + * 5. Try: "/path/filename.ext" + * else: "" + * + * Example: + * language = "en-us;q=0.6,ja;q=0.8,en-ca" + * + * 1. Try: "/path/en-ca/filename.ext" + * 2. Try: "/path/filename_en_ca.ext" + * 3. Try: "/path/ja/filename.ext" + * 4. Try: "/path/filename_ja.ext" + * 5. Try: "/path/en_us/filename.ext" + * 6. Try: "/path/filename_en_us.ext" + * 7. Try: "/path/default/filename.ext" + * 8. Try: "/path/filename_default.ext" + * 9. Try: "/path/filename.ext" + * else: "" + * + */ + +#define LANG_DELIMIT '_' + + int pattern; + char* pDot; + char* pSlash; + + /* PRFileInfo info; */ + struct stat info; + + char lang_modifier[MAX_ACCEPT_LENGTH+1]; + + ACCEPT_LANGUAGE_LIST acceptLanguageList; + int numLang; + int iLang; + int iCase; + + + /* escape in case XP_InitStringDatabase has not been called */ + if (filePath==NULL) { + *existingFilePath = '\0'; + return -1; + } + + pattern = (strstr(filePath,"$$LANGDIR/")!=NULL); + + for ( iCase=1 ; iCase>=0 ; iCase-- ) { + if (iCase==1) { /* iCase=1 tries requested language */ + numLang = XP_AccLangList(language,acceptLanguageList); + } else { /* iCase=0 tries default language */ + numLang = XP_AccLangList(GetDefaultLanguage(),acceptLanguageList); + } + + for ( iLang=0 ; iLang<numLang ; iLang++ ) { + + /* Try: /path/language/filename.ext */ + if (pattern) { + strcpy(existingFilePath,filePath); + strReplace(existingFilePath,"$$LANGDIR",acceptLanguageList[iLang]); + + if (statFileDir(existingFilePath,&info)==0) { + return iCase; + } + + /* + if (PR_GetFileInfo(existingFilePath,&info)==PR_SUCCESS) { + return iCase; + } + */ + } + + /* Try: /path/filename_language.ext */ + { + strcpy(existingFilePath,filePath); + strReplace(existingFilePath,"$$LANGDIR/",emptyString); + pDot = strrchr(existingFilePath,'.'); + pSlash = strrchr(existingFilePath,'/'); + if (pSlash>=pDot) { + pDot = strchr(existingFilePath,'\0'); + } + sprintf(lang_modifier,"%c%s",LANG_DELIMIT,acceptLanguageList[iLang]); + strReplace(pDot,emptyString,lang_modifier); + + if (statFileDir(existingFilePath,&info)==0) { + return iCase; + } + + /* + if (PR_GetFileInfo(existingFilePath,&info)==PR_SUCCESS) { + return iCase; + } + */ + } + } + } + + /* Try: /path/filename.ext */ + { + strcpy(existingFilePath,filePath); + strReplace(existingFilePath,"$$LANGDIR/",emptyString); + + if (statFileDir(existingFilePath,&info)==0) { + return 0; + } + + /* + if (PR_GetFileInfo(existingFilePath,&info)==PR_SUCCESS) { + return 0; + } + */ + } + + /* Else: */ + *existingFilePath = '\0'; + return -1; +} + + + + + + diff --git a/lib/libsi18n/getstrmem.c b/lib/libsi18n/getstrmem.c new file mode 100644 index 00000000..b49b4cc6 --- /dev/null +++ b/lib/libsi18n/getstrmem.c @@ -0,0 +1,123 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> + +#define COMPILE_STRINGS_IN_MEMORY + +static char emptyString[] = ""; + +#ifdef COMPILE_STRINGS_IN_MEMORY + +typedef struct DATABIN { + char* pLibraryName; + char** pArrayOfLibraryStrings; + unsigned numberOfStringsInLibrary; + } DATABIN; + +#include "getstrmem.h" + +#endif + +#if 0 /* Example of getstrmem.h */ + /* It is intended that this header file be generated by program dblink */ + + #define NUM_BUCKETS 2 /* must be power of 2 */ + + /* strings in library libalpha */ + static char* libalpha[] = { + "", + "libalpha string 1", + "libalpha string 2", + "libalpha string 3", + "libalpha string 4", + emptyString }; + + /* strings in library libbeta */ + static char* libbeta[] = { + "", + "libbeta string 1", + "libbeta string 2", + emptyString }; + + /* libraries in bucket for hashKey==0 */ + static struct DATABIN bucket0[] = { + {emptyString, NULL, 0}}; + + /* libraries in bucket for hashKey==1 */ + static struct DATABIN bucket1[] = { + {"libalpha", libalpha, 5}, + {"libbeta", libbeta, 3}, + {emptyString, NULL, 0}}; + + /* array of buckets */ + static struct DATABIN* buckets[NUM_BUCKETS] = { + bucket0, + bucket1 }; + +#endif /* end of example getstrmem.h */ + +#define BUCKET_MASK NUM_BUCKETS-1 + +char* +XP_GetStringFromMemory(char* strLibraryName,int iToken) +{ + /* + * In memory model called by XP_GetStringFromDatabase + * does not use database (nsres, et al.). + * + * This function uses hash table for library lookup + * and direct lookup for string. + * + * This function is thread safe. + */ + +#ifdef COMPILE_STRINGS_IN_MEMORY + + unsigned hashKey; + int found = 0; + unsigned uToken = iToken; + char* cPtr; + DATABIN* pBucket; + + /* calculate hash key */ + hashKey = 0; + cPtr = strLibraryName; + while (*cPtr) { + hashKey += *(cPtr++); + } + hashKey &= BUCKET_MASK; + + /* get bucket for this hash key */ + pBucket = buckets[hashKey]; + + /* search overflow buckets */ + while (*(pBucket->pLibraryName)!='\0') { + if (strcmp(pBucket->pLibraryName,strLibraryName)==0) { + found = 1; + break; + } + pBucket++; + } + + if (!found) { + return emptyString; + } + + if (uToken<=pBucket->numberOfStringsInLibrary) { + return pBucket->pArrayOfLibraryStrings[uToken]; + } else { + /* string token out of range */ + return emptyString; + } + +#else + + return emptyString; + +#endif +} diff --git a/lib/libsi18n/getstrmem.h b/lib/libsi18n/getstrmem.h new file mode 100644 index 00000000..edb6d754 --- /dev/null +++ b/lib/libsi18n/getstrmem.h @@ -0,0 +1,1156 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#define BUCKET_MASK NUM_BUCKETS-1 +typedef struct DATABIN { + char* pLibraryName; + char** pArrayOfLibraryStrings; + unsigned numberOfStringsInLibrary; + } DATABIN; + +/* It is intended that this header file be generated by program dblink */ +static char emptyString[] = ""; + +#define NUM_BUCKETS 32 /* must be a power of 2 */ + +/* strings in library libadmin */ +static char* libadminid[] = {"$DBT: libadmin in memory v1 $"}; +static char* libadmin[] = { + "", + " Help ", + " OK ", + " Reset ", + " Done ", + " Cancel ", + emptyString }; + +/* libraries in bucket for hashKey==0 */ +static struct DATABIN bucket0[] = { + {"libadmin",libadmin,5}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==1 */ +static struct DATABIN bucket1[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==2 */ +static struct DATABIN bucket2[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==3 */ +static struct DATABIN bucket3[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==4 */ +static struct DATABIN bucket4[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==5 */ +static struct DATABIN bucket5[] = { + {emptyString,NULL,0} }; + +/* strings in library userforms */ +static char* userformsid[] = {"$DBT: userforms in memory v1 $"}; +static char* userforms[] = { + "", + "Error: could not open servers list file.<p>\n", + "Error: could not open %s server list file.<p>\n", + "Netscape Server Account", + "Server Account Management for %s", + "Server Account Management", + emptyString }; + +/* libraries in bucket for hashKey==6 */ +static struct DATABIN bucket6[] = { + {"userforms",userforms,5}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==7 */ +static struct DATABIN bucket7[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==8 */ +static struct DATABIN bucket8[] = { + {emptyString,NULL,0} }; + +/* strings in library libaccess */ +static char* libaccessid[] = {"$DBT: libaccess in memory v1 $"}; +static char* libaccess[] = { + "", + "basic-ncsa", + "cannot open database %s", + "basic-ncsa", + "user %s password did not match database %s", + "basic-ncsa", + "cannot open connection to LDAP server on %s:%d", + "basic-ncsa", + "user %s password did not match LDAP on %s:%d", + "acl-state", + "missing realm", + "Unable to allocate ACL List Hash\n", + "ACLEvalBuildContext unable to PERM_MALLOC cache structure\n", + "ACLEvalBuildContext unable to create hash table\n", + "ACLEvalBuildContext unable to allocate ACE Entry\n", + "ACLEvalBuildContext unable to allocate ACE entry\n", + "ACLEvalBuildContext unable to allocate Boundary Entry\n", + "ACLEvalBuildContext failed.\n", + "ACL_EvalTestRights: an interim, absolute non-allow value was encountered. right=%s, value=%d\n", + "LASDnsBuild unable to allocate hash table header\n", + "LASDnsBuild unable to add key %s\n", + "LASDnsBuild unable to add key %s\n", + "LASDnsBuild unable to add key %s\n", + "LASDnsBuild unable to add key %s\n", + "LASDnsBuild unable to add key %s\n", + "LAS DNS build received request for attribute %s\n", + "LASDnsEval - illegal comparator %s\n", + "LASDnsEval unable to allocate Context struct\n\n", + "LASDnsEval unable to get session address %d\n", + "LASDnsEval unable to get DNS - error=%s\n", + "LAS Group Eval received request for attribute %s\n", + "LASGroupEval - illegal comparator %s\n", + "LASGroupEval - ran out of memory\n", + "LASGroupEval unable to get session address %d\n", + "LASGroupEval unable to get session address %d\n", + "LASGroupEval - couldn't locate getter for auth-user\n", + "LASGroupEval - Attribute getter for auth-user failed\n", + "LASGroupEval - Attribute getter didn't set auth-user\n", + "Check group membership of user \"%s\" for group \"%s\"\n", + "LDAPU_SUCCESS for group \"%s\"\n", + "LDAPU_FAILED for group \"%s\"\n", + "LAS_EVAL_FALSE\n", + "LAS_EVAL_TRUE\n", + "LASIpTreeAlloc - no memory\n", + "IP LAS unable to allocate tree node\n", + "IP LAS unable to allocate tree node\n", + "LAS IP build received request for attribute %s\n", + "LASIpEval - illegal comparator %s\n", + "LASIpEval unable to get session address - error=%s\n", + "LASIpEval unable to allocate Context struct\n\n", + "LASIpEval - reach 32 bits without conclusion value=%s", + "LAS Program Eval received request for attribute %s\n", + "LASProgramEval - illegal comparator %s\n", + "LASProgram unable to get session address %d\n", + "bin", + "LASProgramEval: request not of type admin or bin, passing.\n", + "LASProgramEval: check if program %s matches pattern %s.\n", + "LASProgramEval: Invalid wildcard expression %s.\n", + "LAS_EVAL_FALSE\n", + "LAS_EVAL_TRUE\n", + "Unexpected attribute in dayOfWeek - %s\n", + "Illegal comparator for dayOfWeek - %s\n", + "Unexpected attribute in timeOfDay - %s\n", + "LAS User Eval received request for attribute %s\n", + "LASUserEval - illegal comparator %s\n", + "LASUserEval - ran out of memory\n", + "LASUserEval unable to get session address %d\n", + "LASUserEval unable to get session address %d\n", + "LASGroupEval - couldn't locate getter for auth-user\n", + "LASGroupEval - Attribute getter for auth-user failed\n", + "LASGroupEval - Attribute getter didn't set auth-user\n", + "Check if uid == user (i.e. check \"%s\" == \"%s)\"\n", + "SUCCESS for user \"%s\"\n", + "FAILED for user \"%s\"\n", + "LAS_EVAL_FALSE\n", + "LAS_EVAL_TRUE\n", + "", + "LASProgram unable to get request address - error=%s", + "LASProgram rejecting request for program %s from pattern %s", + "ACL_CacheFlush: unable to parse file \"%s\"\n", + "ACL_CacheFlush: unable to concatenate ACL list \"%s\"\n", + "ACL_CacheFlush: unable to open and process the magnus file \"%s\"\n", + "Illegal comparator for timeOfDay - %s\n", + "ACL_EvalBuildContext unable to create hash table\n", + "ACL_EvalBuildContext unable to PERM_CALLOC cache structure\n", + "ACL_EvalBuildContext unable to allocate ACE entry\n", + "ACL_EvalBuildContext unable to allocate auth pointer array\n", + "ACL_EvalBuildContext unable to allocate auth plist\n", + "ACL_EvalTestRights: an interim, absolute non-allow value was encountered. right=%s, value=%s\n", + "ACL_INTEvalTestRights: call to ACL_EvalBuildContext returned failure status\n", + "ACL_ModuleRegister: module name is missing\n", + "ACL_ModuleRegister: call to module init function returned a failed status\n", + "ACL_GetAttribute: couldn't determine method for %s\n", + "ACL_GetAttribute: couldn't locate getter for %s", + "ACL_GetAttribute: attr getter failed to get %s", + "ACL_GetAttribute: attr getter failed to get %s", + "ACL_GetAttribute: All attribute getters declined for attr %s", + "ACL_DatabaseRegister: dbtype for database \"%s\" is not defined yet!", + "ACL_DatabaseRegister: database name is missing", + "Error reading the DB Map File: %s. Reason: %s", + "URL is missing for database %s", + "Invalid property value pair for database %s", + "\"default\" database must be an LDAP database", + "Multiple \"default\" databases are being registered", + "\"default\" LDAP database must be registered", + "LASGroupEval unable to get database name - error= %s", + "received invalid program expression %s", + "parse_ldap_url: database url is missing", + "parse_ldap_url: database name is missing", + "parse_ldap_url: error in parsing ldap url. Reason: %s", + "ldap password check: unable to get database name - error=%s", + "ldap password check: unable to get parsed database %s", + "ldap password check: couldn't initialize connection to LDAP. Reason: %s", + "ldap password check: LDAP error: \"%s\"", + "get_user_ismember_ldap unable to get database name - error=%s", + "get_user_ismember_ldap unable to get parsed database %s", + "ldap password check: couldn't initialize connection to LDAP. Reason: %s", + "get_user_ismember_ldap: group %s does not exist", + "get_user_ismember_ldap: LDAP error: \"%s\"", + "ACL_LDAPDatabaseHandle: %s is not a registered database", + "ACL_LDAPDatabaseHandle: %s is not an LDAP database", + "ACL_LDAPDatabaseHandle: out of memory", + "ACL_LDAPDatabaseHandle: couldn't initialize connection to LDAP. Reason: %s", + "ACL_LDAPDatabaseHandle: couldn't bind to LDAP server. Reason: %s", + "insufficient dynamic memory", + "error opening file, %s: %s", + "duplicate definition of %s", + "file %s, line %s: duplicate definition of %s", + "file %s, line %s: syntax error", + "file %s, line %s: %s is undefined", + "in acl %s, %s %s is undefined", + "database %s: error accessing %s", + "%s", + "file %s, line %s: invalid syntax", + "file %s, line %s: syntax error at \"%s\"", + "realm %s is not defined", + "error code = %d", + "internal ACL error", + "invalid argument", + "ACL_DatabaseRegister: dbtype for database \"%s\" is not defined yet!", + "couldn't determine dbtype from: %s", + "Failed to register database %s", + "ACL call returned failed status", + "file %s: ACL IO error - %s", + "acl_user_exists: out of memory", + "acl_user_exists: user doesn't exist anymore", + "acl_user_exists: plist error", + emptyString }; + +/* libraries in bucket for hashKey==9 */ +static struct DATABIN bucket9[] = { + {"libaccess",libaccess,146}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==10 */ +static struct DATABIN bucket10[] = { + {emptyString,NULL,0} }; + +/* strings in library frame */ +static char* frameid[] = {"$DBT: frame in memory v1 $"}; +static char* frame[] = { + "", + "<TITLE>Not Found</TITLE><H1>Not Found</H1> The requested object does not exist on this server. The link you followed is either outdated, inaccurate, or the server has been instructed not to let you have it. ", + "Please inform the site administrator of the <A HREF=\"%s\">referring page</A>.", + "Your browser sent a request that this proxy could not understand.", + "Proper authorization is required for the administration of this proxy.\nEither your browser does not perform authorization, or your authorization\nhas failed.", + "Username authentication is required for using this proxy.\nEither your browser does not perform proxy authorization, or your\nauthorization has failed.", + "The proxy's access control configuration denies access to\nthe requested object through this proxy.", + "The proxy has encountered an internal error which prevents it from\nfulfilling your request. The most likely cause is a misconfiguration.\nPlease ask the administrator to look for messages in the proxy's error log.", + "This proxy server does not implement the requested method.", + "An error has occurred on the proxy server.", + "Your browser sent a query this server could not understand.", + "Proper authorization is required for this area. Either your browser does not perform authorization, or your authorization has failed.", + "Your client is not allowed to access the requested object.", + "This server has encountered an internal error which prevents it from fulfilling your request. The most likely cause is a misconfiguration. Please ask the administrator to look for messages in the server's error log.", + "This server does not implement the requested method.", + "An error has occurred.", + "Your browser sent a message this server could not understand.", + "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>\nThis document has moved to a new <a href=\"%s\">location</a>. Please update your documents and hotlists accordingly.</BODY></HTML>", + "<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n<BODY><H1>%s</H1>\n%s\n</BODY></HTML>", + "process-uri-objects", + "cannot find template %s", + "process-uri-objects", + "no partial path after object processing", + "find-service", + "invalid shexp %s", + "find-service", + "invalid shexp %s", + "handle-processed", + "no way to service request for %s", + "finish-socks-request", + "close failed (%s), csd=%d", + "This beta software has expired.\n", + "mr_accept(%d)", + "Error issuing read on accept socket", + "acb_accept_connection(%d)", + "Error getting accept socket (%d)", + "Error in accept! (%d, %s)", + "Error creating new accept request", + "accepted connection: %d (NSPR %d)", + "Error creating new session structure", + "accel_read_request()", + "Error allocating request read buffer", + "Error issuing async read request", + "acb_read_request(%d, bytes %d)", + "acb_read_req(1 session = %d)", + "Error reading request (%d, %s)", + "Client aborted connection", + "Error reading request", + "Error creating new request", + "error occurred, closing %d, io was for %d", + "accel_async_scan_headers()", + "out of memory: accel_async_scan_headers", + "out of memory: accel_async_scan_headers", + "Error during async read (%d, %s)", + "scan-headers reports: line too long", + "scan-headers reports: too many headers", + "Error reading headers", + "scan-headers reports: header missing terminator (an empty line)", + "scan-headers reports: header was empty", + "name without value: got line \"%s\"", + "accel_send_plain_file()", + "accel_send_plain_file() - found request %d", + "Parse headers lost the URI!", + "accel_send_plain_file() - found uri %s", + "accel_send_plain_file() - found in cache?", + "malloc died!", + "Error writing back file\n", + "acb_send_plain_file(%d)", + "Error writing in acb_send_plain_file (%d, %s)", + "acb_close_connection(%d)", + "Errored IO in acb_close_connection (%d, %s)", + "Unable to close socket %d", + "accel-cache-insert: Error allocating entry", + "cache-init: server cache disabled", + "accel_file_cache: Error initializing file cache", + "accel_file_cache: Error creating cache", + "accel_file_cache_init: CacheHashSize < %d, using %d", + "accel_file_cache_init: CacheHashSize > %d, using %d", + "accel_file_cache_init: MaxNumberOfCachedFiles < %d, using %d", + "accel_file_cache_init: MaxNumberOfCachedFiles > %d, using %d", + "accel_file_cache_init: MaxNumberOfOpenCachedFiles < %d, using %d", + "accel_file_cache_init: MaxNumberOfOpenCachedFiles > %d, using %d", + "accel_file_cache: Error initializing file cache", + "file-cache: enabled = %s ", + "on", + "off", + "file-cache: MaxNumberOfCachedFiles %d (0x%x)", + "file-cache: CacheHashSize %d (0x%x)", + "file-cache: MaxNumberOfOpenCachedFiles %d (0x%x)", + "accel-cache-insert: Error allocating entry", + "file-cache-cleanup: munmap failed (%s)", + "file-cache-cleanup: found mmapped file \non system without mmap", + "file_cache_init: MaxTotalCachedFileSize < %d, using %d", + "file_cache_init: MaxTotalCachedFileSize > %d, using %d", + "file cache using mmap flags 0x%x", + "file cache using mmap prots 0x%x", + "file-cache-init: could not create lock", + "file-cache: unable to create temporary directory %s.\n", + "file-cache: unable to create temporary directory %s.\n", + "file-cache: GetTempPath() Cannot find temp directory to store file!", + "file-cache-init: set max cached file size to %d", + "file-cache-init: could not create lock", + "file_cache_destroy()", + "file-cache: Unable to get temp file name. Error %s", + "file-cache: Unable to get temp file name. Error %s", + "file-cache-insert: Error allocating entry", + "file-cache-create: Error opening file %s (%s)", + "file-cache-create: Error stat()ing file %s (%s)", + "file-cache-create: Error mmap()ing file %s (%s)", + "file-cache-create: malloc failure", + "file-cache-create: error case failed to munmap(%d, %d) (%s)", + "file-cache-valid: cannot stat %s", + "dir change: invalidating %s (%d)", + "file-cache: asynchronous file change notification failed.", + "dir change: offset %d, action %d, len %d, name %s", + "unable to check async file status", + "ReadDirectoryChangesW failure- unable to continue watching %s. Error is %s", + "file-cache-add-watch failure- unable to open directory %s. Error %s", + "ReadDirectoryChangesW failure- unable to start watching %s. Error %s", + "func_exec", + "no handler function given for directive", + "func_exec", + "cannot find function named %s", + "handle-request", + "method without URI", + "http-parse-request", + "while scanning HTTP headers, %s", + "handle-request", + "read from %s failed, error is %s", + "handle-request", + "request too long", + "start-http-response", + "write failed (%s)", + "start-http-response", + "write failed (%s)", + "http-status", + "%d is not a valid HTTP status code", + "finish-request", + "close failed (%s)", + "Unable to close socket for writing", + "os has %d objects", + "obj %d has no hash table at %d", + "obj %d has no param", + "obj %d name %s value %s", + ".....directives %d.......", + ".....directive %d", + ".......instance %d", + "...........param name %s value %s", + "ClearConfigurationParameters:RegCreateKey %s", + "ClearConfigurationParameters:RegEnumKey %s failed", + "obj.conf line %d: error in filter file \"%s\" at line %d: %s", + "obj.conf line %d: error in filter file \"%s\" at line %d: %s", + "Unable to allocate Subject property list.\n", + "Unable to set session ptr in Subject property list - error=%d\n", + "Unable to set request ptr in Subject property list - error=%d\n", + "file-cache-valid: cannot stat %s", + "file-cache-create: Error stat()ing file %s (%s)", + emptyString }; + +/* libraries in bucket for hashKey==11 */ +static struct DATABIN bucket11[] = { + {"frame",frame,156}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==12 */ +static struct DATABIN bucket12[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==13 */ +static struct DATABIN bucket13[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==14 */ +static struct DATABIN bucket14[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==15 */ +static struct DATABIN bucket15[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==16 */ +static struct DATABIN bucket16[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==17 */ +static struct DATABIN bucket17[] = { + {emptyString,NULL,0} }; + +/* strings in library admserv */ +static char* admservid[] = {"$DBT: admserv in memory v1 $"}; +static char* admserv[] = { + "", + "Unauthorized host", + "Invalid URL", + "no filename extension", + "unrecognized type", + "<title>Internal Error</title>\n<h1>Internal Error</h1>\nThe administration server was unable to fulfill your request.<p>\n", + "Reason:", + emptyString }; + +/* strings in library libir */ +static char* libirid[] = {"$DBT: libadmin in memory v1 $"}; +static char* libir[] = { + "", + "An I/O error occurred before all form data could be read.", + emptyString }; + +/* libraries in bucket for hashKey==18 */ +static struct DATABIN bucket18[] = { + {"admserv",admserv,6}, + {"libir",libir,1}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==19 */ +static struct DATABIN bucket19[] = { + {emptyString,NULL,0} }; + +/* strings in library httpdaemon */ +static char* httpdaemonid[] = {"$DBT: httpdaemon in memory v1 $"}; +static char* httpdaemon[] = { + "", + "Error in ConvertThreadToFiber", + "Error in ConvertThreadToFiber", + "Error in md_start_system", + "Error in CreateFiber - idlefiber", + "Error in GetQueuedCompletionStatus", + "Error creating completion port", + "Could not SetHandleNonInheritable (%s)", + "Error accept/read new conn", + "Error in Respond()", + "Error in RespondCompleted()", + "daemon: unable to fork new process (%s)\n", + "daemon: setsid failed (%s)\n", + "daemon: can't log pid to %s (%s)\n", + "warning: could not set group id to %d (%s)\n", + "warning: could not set user id to %d (%s)\n", + "warning: daemon is running as super-user\n", + "could not determine current user name\n", + "error: chroot to %s failed (%s)\n", + "WARNING! Netscape executable and library have different versions.\n", + "startup: listening to %s://%s, port %d as %s\n", + "startup: listening to %s://%s, port %d as %s\n", + "seminit failed (%s)\n", + "Using single threaded accepts.", + "Using multi threaded accepts.", + "Using partial single threaded accepts.", + "This machine has %d processors.", + "Error calling thr_seconcurrency(%d)- (%s)", + "Set conncurrency to %d.", + "can't fork new process (%s)", + "This beta software has expired.\n", + "can't create IPC pipe (%s)", + "write to wakeup pipe failed (%s)", + "select thread miss", + "startup failure: could not bind to port %d (%s)\n", + "startup failure: could not bind to port %d, IP address %s (%s)\n", + emptyString }; + +/* libraries in bucket for hashKey==20 */ +static struct DATABIN bucket20[] = { + {"httpdaemon",httpdaemon,35}, + {emptyString,NULL,0} }; + +/* strings in library dsgw */ +static char* dsgwid[] = {"$DBT: dsgw in memory v1 $"}; +static char* dsgw[] = { + "", + "Unknown HTTP request method", + "Invalid or incomplete HTML form data", + "Out of memory", + "Required query/form input is missing", + "Illegal character in file path", + "Bad or missing configuration file", + "Unable to initialize LDAP", + "An error occurred while contacting the LDAP server", + "Unknown search object type", + "Unknown attribute label", + "Unknown match prompt", + "No search filters for object type", + "Unable to open HTML template file", + "Unknown search mode - use \"smart\", \"complex\", \"pattern\", or \"auth\"", + "Distinguished Name missing in URL", + "Unknown scope in URL (should be base, sub, or one)", + "Unrecognized URL or unknown error", + "Bad URL format", + "Internal error", + "Unable to write template index file", + "Unable to open template index file", + "Unable to read directory", + "LDAP SSL initialization failed (check the security path)", + "For the Users and Groups forms to work over SSL with the Administration Server, you must at least temporarily activate SSL through the <B>Admin Preferences|Encryption On/Off</B> form", + "Authentication credentials not found in authentication database", + "Error retrieving data from the authentication database", + "Your authentication credentials have expired", + "Unable to create authentication credentials", + "No distinguished name was provided when retrieving credentials", + "Cannot open authentication database", + "Could not append data to the authentication database", + "No Directory Manager is defined", + "No search string was provided. Please try again", + "Too many arguments on one line in the config. file", + "Failed to initialize Windows Sockets", + "Authentication credentials could not be obtained from the Administration Server", + "Distinguished Name missing in ldapdb:// URL", + "Unrecognized URL or unknown error", + "Bad URL format", + "An error occurred while initializing the local LDAP database", + "Unknown directory service type - use \"local\" or \"remote\"", + "An error occurred while reading the db configuration file", + "NSHOME/userdb path was NULL", + "The directory service configuration could not be updated.", + "The entry could not be read from the directory.", + "The LDAP database could not be erased.", + "You may not change entries besides your own.", + "Problem", + "Authentication Problem", + ".\n<P>You must re-authenticate before continuing.\n", + ".\n<P>You must re-authenticate before continuing.\n", + "unknown error", + "The operation was successful.", + "An internal error occurred in the server. This usually\nindicates a serious malfunction in the server and should be\nbrought to the attention of your server administrator.", + "The server could not understand the request which was sent to\nit by the gateway.", + "A time limit was exceeded in responding to your request. If\nyou are searching for entries, you may achieve better results\nif you are more specific in your search.", + "A size limit was exceeded in responding to your request. If\nyou are searching for entries, you may achieve better results\nif you are more specific in your search, because too many entries\nmatched your search criteria.", + "The gateway attempted to authenticate to the server using\na method the server does not understand.", + "The gateway attempted to authenticate to the server using an\nauthentication method which the server does not support. ", + "Your request could not be fulfilled, probably because the server\nthat was contacted does not contain the data you are looking\nfor. It is possible that a referral to another server was\nreturned but could not be followed. If you were trying to make\nchanges to the directory, it may be that the server that holds\nthe master copy of the data is not available.", + "Your request exceeded an administrative limit in the server.", + "A critical extension that the gateway requested is not available in this server.", + "The server was unable to process the request, because the\nrequest referred to an attribute which does not exist in the\nentry.", + "The server was unable to fulfill your request, because the\nrequest violates a database constraint.", + "The server could not add a value to the entry, because that\nvalue is already contained in the entry.", + "The server could not locate the entry. If adding a new entry,\nbe sure that the parent of the entry you are trying to add exists.\nIf you received this error while searching or viewing an entry, it indicates that the\nentry which was being searched for does not exist.", + "A distinguished name was not in the proper format. ", + "The entry you attempted to authenticate as does not have a\npassword set, or is missing other required authentication\ncredentials. You cannot authenticate as that entry until the\nappropriate attributes have been added by the directory manager. ", + "The password (or other authentication credentials) you supplied\nis incorrect. If you just changed your password, you might try exiting your browser and connecting again.", + "You do not have sufficient privileges to perform the operation. ", + "The server is too busy to service your request. Try again\nin a few minutes.", + "The LDAP server could not be contacted.", + "The server was unwilliing to process your request. Usually,\nthis indicates that serving your request would put a heavy load\non the server. It may also indicate that the server is not\nconfigured to process your request. If searching, you may wish\nto limit the scope of your search.", + "The directory server could not honor your request because it\nviolates the schema requirements. Typically, this means that you\nhave not provided a value for a required field. It could also mean\nthat the schema in the directory server needs to be updated.", + "The directory server will not allow you to delete or rename\nan entry if that entry has children. If you wish to do this, you\nmust first delete all the child entries.", + "The server was unable to add a new entry, or rename an existing\nentry, because an entry by that name already exists.", + "Your request would affect several directory servers.", + "The directory server could not be contacted. Contact your\ndirectory server administrator for assistance.", + "An error occured while sending data to the server.", + "An error occured while reading data from the server.", + "The server did not respond to the request. \nThe request timed out.", + "The server does not support the authentication method used\nby the gateway.", + "The search filter constructed by the gateway was in error.", + "The operation was cancelled at your request.", + "An internal error occurred in the library - a parameter was\nincorrect.", + "A connection to the directory server could not be opened. Contact your\ndirectory server administrator for assistance.", + "An unknown error was encountered.", + "Entry Already Exists", + "An entry named ", + "onMouseOver=\"window.status='Click here to view this entry'; return true\"", + " already exists.<P>Please choose another name and/or location.\n<P>\n", + "Parent entry does not exist", + "You cannot add an entry by the name:<P><B>%s</B>,<P>\nbecause the parent of that entry does not exist.<P>\nBefore you can add this entry, you must first add\n", + "its parent.\n", + "an entry named:<P><B>%s</B>.\n", + "Warning: no authentication (continuing)...\n", + "%s Directory Entry", + "<PRE>Entry DN: %s</PRE><P>\n", + "Changes to <B>%s</B> have been saved.", + "<B>%s</B> has been added.", + "<B>%s</B> has been deleted.", + "Renamed <B>%s</B> to <B>%s</B>.", + "<P><B>Note:</B> because you %s the entry you were \nauthenticated as, it was necessary to discard your \nauthentication credentials. You will need to authenticate \nagain to make additional changes.\n", + "deleted", + "renamed", + "changed the password of", + "Attribute %s was changed<BR>\n", + " NOT ASCII (%ld bytes)\n", + "No values were entered. Please try again.\n", + "No changes were made.\n", + "<P>Sending %s to the directory server...\n", + "information", + "changes", + "<P>Successfully added entry.\n", + "<P>Successfully edited entry. Your changes have been saved.\n", + "<P>Successfully deleted entry.\n", + "<PRE>The new name for the entry is: %s\n</PRE><HR>\n", + "<P>Successfully renamed entry.\n", + "You must provide the old password.", + "You must provide a new password. Please try again", + "The new and confirming passwords do not match. Please try again", + "<BR>The %s <B>%s</B> is already in use. Please choose a different one.<BR>\n", + "missing form data element \"%.100s\"", + "Initializing config info", + "Cannot open file.", + "Malformed dbconf file.", + "Missing property name in dbconf file.", + "Out of memory.", + "Missing directive in dbconf file.", + "Cannot open config file \"%s\"", + "Missing argument for \"authlifetime\" directive\n", + "Missing argument for \"dirmgr\" directive\n", + "Missing argument for \"baseurl\" directive\n", + "Bad URL provided for \"baseurl\" directive - the base DN is missing\n", + "parsing baseurl directive", + "Bad URL provided for \"baseurl\" directive - not an \"ldap://\" URL\n", + "\"ldaps://\" URLs are not yet supported\n", + "Missing arguments for \"template\" directive\n", + "Missing argument for \"sslrequired\" directive\n", + "Unknown argument to \"sslrequired\" directive (should be \"never\", \"whenauthenticated\", \"always\")\n", + "Missing argument for \"securitypath\" directive\n", + "Missing argument for \"location-suffix\" directive\n", + "Three arguments are required for the \"location\" directive\n", + "At least two arguments are required for the \"newtype\" directive\n", + "Unknown location in \"newtype\" directive\n", + "Three or four arguments are required for the \"tmplset\" directive\n", + "Four arguments are required for the \"attrvset\" directive\n", + "Missing argument for \"charset\" directive\n", + "Missing argument for \"ClientLanguage\" directive\n", + "Missing argument for \"AdminLanguage\" directive\n", + "Missing argument for \"DefaultLanguage\" directive\n", + "Missing filename for \"include\" directive\n", + "Unknown directive in config file\n", + "<= erase_db could not open lcache.conf file \"%s\"\n", + "<FONT SIZE=\"+1\">\n<P>The database has been deleted. Creating new database... \n</FONT>\n ", + "<FONT SIZE=\"+1\">\n<P>The database could not be deleted \n</FONT>\n ", + "<= app_suffix could not open ldif file \"%s\"\n", + "<= app_suffix could not open tmp file \"%s\"\n", + "Unable to rename %s to %s", + "null pointer returned by dbconf_read_default_dbinfo().", + "Bad \"ldapdb\" URL - the base DN is missing\n", + "Bad \"ldapdb\" URL\n", + "Bad URL provided for \"baseurl\" directive - the base DN is missing\n", + "parsing baseurl directive", + "Bad URL provided for \"baseurl\" directive - not an \"ldap:// or ldapdb://\" URL\n", + "\"ldaps://\" URLs are not yet supported\n", + "No value given for binddn", + "No value given for bindpw", + "There is no default directory service defined in the dbswitch.conf file", + "Cannot open config file \"%s\" for writing", + "Unable to rename %s to %s", + "config file %s: ", + "config file %s: line %d: ", + "max %d", + " OK ", + "Close Window", + "Go Back", + "{crypt}LOCKED [%s GMT]", + "Return to Main", + "Return to Main", + " Help ", + "Help", + " Help ", + "Help is not yet available.", + "Help", + "Close Window", + "Close Window", + "missing ?template", + "Authenticate...", + "Discard authentication credentials (log out)?", + "You did not supply a search string", + "The first step in authenticating to the directory is identifying\nyourself.<br>Please type your name:", + "Continue", + "Continue", + "Cancel", + "Authenticate as directory manager\">  (only available to Directory Administrators)\n", + "Authenticate...", + "Discard authentication credentials?", + "Password for <b>%s</b>: ", + "Continue", + "Continue", + "Cancel", + "Authenticate (log in) to the directory", + "You are about to authenticate to the directory as \n", + "To complete the authentication process, you should\ntype your password.\n", + "Before you can edit or add entries, you must authenticate\n(log in) to the directory. This window will guide\nyou through the steps of the authentication\nprocess.\n", + "From this screen you may authenticate, or log in, \nto the directory. You will need to authenticate\nbefore you can modify directory entries. If you\nattempt to modify an entry without authenticating,\nyou will be asked to log in.\n", + "Authentication Status", + "<form>\nYou are currently authenticated to the directory as ", + ".\nIf you wish to discard your authentication credentials and log out of the directory, click on the button below.", + "Discard Authentication Credentials (log out)", + "Your authentication credentials for ", + "have expired.\n<HR>\n", + "Currently, you are not authenticated to the directory.<HR>\n", + "missing \"%s=\"", + "unknown \"%s=%s\"", + "unknown option %s", + "unknown syntax=%s\n", + "** HTML type \"%s\" not supported **<BR>\n", + "no entries", + "1 entry", + "%d entries", + "where the ", + "Edit", + "Save Changes", + "modify", + "add", + "Delete", + "Delete this entry?", + "Rename", + "Enter a new name for this entry:", + "Edit As", + "missing %s=", + "Close Window", + "Edit...", + "missing \"%s=\"\n", + "unknown set \"%s\"\n", + "unknown syntax \"%s\"\n", + "Re-Authenticate", + "Close Window", + "Do you really want to ", + "?", + " OK ", + " OK ", + " Reset ", + " Done ", + " Cancel ", + "found another IF (nested IFs are not supported)", + "found ELSE but didn't see an IF", + "found ELSE after ELSE (expecting ENDIF)", + "found ELIF but didn't see an IF", + "found ELIF after ELSE (expecting ENDIF)", + "found ENDIF but didn't see an IF", + "<BR><B>template error:</B> %s<BR>\n", + "ldap_init/lcache_init attempted before config file read", + "not running under the administration server", + "Could not initialize permissions", + "Could not map username to a DN (error from admin server)", + "Could not get current username", + "Could not get current user password", + "Error: %s", + "Note: there is no display template for this type of entry available, so it is\ndisplayed below using a default method.", + "Invalid user id or NULL LDAP handle", + "no match for user id", + "more than one match for user id", + "the entire directory", + "Two arguments are required for the \"includeset\" directive\n", + "An error occurred while trying to access the database", + "All references to entry could not be changed", + "<P>Successfully removed all references to deleted entry.", + "<P>Successfully updated all references to renamed entry.", + "<P>Removing references to <B>%s</B>...", + "<P>Updating references to <B>%s</B>...", + "<P>removing %s from %s \n", + "The directory server could not be contacted. One common configuration error when using LDAP over SSL is that the certificate used by the directory server must be marked as trusted. Contact your directory server administrator for further assistance.", + "A connection to the directory server could not be opened. One common configuration error when using LDAP over SSL is that the certificate used by the directory server must be marked as trusted. Contact your directory server administrator for further assistance.", + "Editing", + "Adding", + "Deleting", + "Renaming", + "Servers are not configured for CAL", + "Cannot update config file \"%s\"\n", + "Missing argument for \"locationurl\" directive\n", + "Invalid location url\n", + "<= app_suffix could not parse LDIF \"%s\"\n", + emptyString }; + +/* libraries in bucket for hashKey==21 */ +static struct DATABIN bucket21[] = { + {"dsgw",dsgw,285}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==22 */ +static struct DATABIN bucket22[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==23 */ +static struct DATABIN bucket23[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==24 */ +static struct DATABIN bucket24[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==25 */ +static struct DATABIN bucket25[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==26 */ +static struct DATABIN bucket26[] = { + {emptyString,NULL,0} }; + +/* strings in library base */ +static char* baseid[] = {"$DBT: base in memory v1 $"}; +static char* base[] = { + "", + "insufficient memory to create hash table", + "insufficient memory to create hash table", + "cache_destroy: cache tables appear corrupt.", + "unable to allocate hash entry", + "cache_insert: unable to create cache entry", + "HTTP/1.0 200 OK\nContent-type: text/html\n\n", + "<H2>Netscape cache status report</H2>\n", + "No caches on system<P>", + "<H2>%s cache</H2>\n", + "Cache hit ratio: %d/%d (%f)</P>\n</P>\n", + "Cache size: %d/%d</P>\n</P>\n", + "Hash table size: %d</P>\n</P>\n", + "mru : %d</P>\nlru : %d</P>\n", + "<UL><TABLE BORDER=4> <TH>Bucket</TH> <TH>Address</TH> <TH>Key</TH> <TH>Access Count</TH> <TH>Delete</TH> <TH>Next</TH> <TH>LRU</TH> <TH>MRU</TH> <TH>Data</TH>\n", + "munmap failed (%s)", + "munmap failed (%s)", + "close failed (%s)", + "daemon: unable to fork new process (%s)\n", + "daemon: setsid failed (%s)\n", + "daemon: can't log pid to %s (%s)\n", + "warning: could not set group id to %d (%s)\n", + "warning: could not set user id to %d (%s)\n", + "warning: daemon is running as super-user\n", + "could not determine current user name\n", + "error: chroot to %s failed (%s)\n", + "", + ", address %s", + "warning: statistics disabled (%s)\n", + "security handshake timed out for pid %d", + "warning: statistics disabled (%s)\n", + "secure handshake failed (code %d)\n", + "accept failed (%s)", + "warning: statistics disabled (%s)\n", + "select thread miss", + "keepalive worker awoken with no work to do", + "could not create new thread: %d (%s)", + "wait for sema succeeded, but nothing to dequeue", + "queue-sema creation failure", + "error getting processor info for processor %d", + "Error binding to processor %d", + "bound process %d to processor %d", + "Netscape server is not explicitly binding to any processors.", + "cache monitor exited", + "cache batch update daemon exited", + "Using single threaded accepts.", + "Using multi threaded accepts.", + "Using partial single threaded accepts.", + "This machine has %d processors.", + "Error calling thr_seconcurrency(%d)- (%s)", + "Set conncurrency to %d.", + "WARNING! Netscape executable and library have different versions.\n", + "", + "", + "seminit failed (%s)\n", + "This beta software has expired.\n", + "Cache monitor respawned", + "Cache batch update daemon respawned", + "can't find empty statistics slot", + "can't fork new process (%s)", + "assert failed! %s\n", + "mr_table_init()", + "malloc failed", + "malloc failed!", + "mr_add_io(%d, type %d, file %d)", + "mr_add_io - stage 1", + "mr_add_io - stage 2", + "mr_add_io found invalid IO type %d", + "mr_add_io - adding timeout", + "Out of memory!\n", + "done with mr_add_io", + "mr_del_io(%d, type %d, file %d)", + "mr_del_io found invalid IO type %d", + "mr_lookup_io(%d)", + "mr_async_io(%d, %d bytes, file %d)", + "malloc failure adding async IO", + "Error adding async io!", + "Cannot seek for read!", + "read failure! (%d, %s)", + "do_read read %d bytes for file %d", + "Cannot seek for write!", + "writev failure! (%d, %s)", + "write failure! (%d, %s)", + "do_write wrote %d bytes for file %d", + "do_timeout(mrp %d)", + "do_timeout: found IO (timer=%d, time=%d)", + "error deleting io", + "timeout callback failure for %d\n", + "mr_get_event(%d) - outstanding io %d", + "mr_get_event: Waiting for reads on FD:", + "mr_get_event: Waiting for writes on FD:", + " %d", + " %d", + "mr_get_event set no timeout", + "mr_get_event set timeout to: %d.%d sec", + "error in select (%d, %s)", + "mr_get_event() - select found %d", + "error looking up IO fd %d", + "read failed for fd %d", + "error deleting io", + "callback failure for %d\n", + "error looking up IO fd %d", + "writing: header len %d, writelen %d, total %d", + "write failed for fd %d", + "error deleting io", + "callback failure for %d\n", + "Error creating dns cache", + "dns_cache_init: hash_size <= 0, using %d", + "dns_cache_init: cache-size <= %d, using %d", + "dns_cache_init: cache-size is %d is too large, using %d.", + "dns_cache_init: expire_time <= 0, using %d", + "dns_cache_init: expire is %d is too large, using %d seconds.", + "Error creating dns cache", + "dns-cache-insert: Error allocating entry", + "dns-cache-insert: malloc failure", + "successful server startup", + "%s B%s", + "Netscape executable and shared library have different versions", + " executable version is %s", + " shared library version is %s", + "error reporting shutting down", + "warning", + "config", + "security", + "failure", + "catastrophe", + "info", + "verbose", + "event_handler:Failed to wait on events %s", + "could not wait on resume event event (%s)", + "dlopen of %s failed (%s)", + "dlopen of %s failed (%s)", + "The server is terminating due to an error. Check the event viewer for the error message. SERVER EXITING!", + "Terminating the server %s", + "kill_server:cannot open server event %s", + "kill_server:cannot set server event %s", + "error: could not get socket (%s)\n", + "error: could not set socket option (%s)\n", + "Terminating Service:error: could not bind to address %s port %d (%s)\n", + "Terminating Service:error: could not bind to port %d (%s)\n", + "SetHandleNonInheritable: could not duplicate socket (%s)", + "SetHandleNonInheritable: closing the original socket failed (%s)", + "Could not SetHandleInformation (%s)", + "Terminating Service:Failure: Could not open statistics file (%s)\n", + "Could not set Thread Local Storage Value for thread at slot %d", + "secure handshake failed (code %d)\n", + "accept failed %d (%s)", + "Failed to pulse Event %d %s", + "Failed to send MobGrowth Event to parent %s", + "Pulsing MobRespawn Event %d", + "respawn thread pool to %d (%d)", + "Could not open event to signal rotate application. Could not create the MoveLog event:%s", + "Failed to send MoveLog Event to rotate app %s", + "growing thread pool from %d to %d", + "Could not open the ServiceControlManager, Error %d", + "StartNetsiteService:Could not open the service %s: Error %d", + "StartNetsiteService:Could not start the service %s", + "Service Startup: Could not allocate security descriptor", + "Service Startup: Could not init security descriptor", + "Service Startup: Could not set the security Dacl", + "Terminating Service:WinSock init failed: %s", + "Httpd Server Startup failed: %s", + "can't find empty statistics slot", + "NT daemon: could not create new thread %d", + "Service Startup Failure. Terminating Service:Could not create event %d:%s", + "Service Startup Error. Could not create the MoveLog event:%s", + "Failed to wait on Event objects %s", + "Failed to wait on Event objects %s", + "pipebuf_buf2sd: pipebuf_grab IO_ERROR %d", + "pool-init: memory pools disabled", + "pool-init: free_size <= 0, using %d", + "pool-create-block: out of memory", + "pool-create: out of memory", + "pool-create: out of memory", + "pool-malloc: out of memory", + "FREE() used where PERM_FREE() should have been used- problem corrected and supressing further warnings.", + "regex error: %s (regex: '%s')", + "can't create IPC pipe (%s)", + "write to wakeup pipe failed (%s)", + "flushing %d connections; current %d; tot %d", + "accept failed (%s)", + "Error creating time cache", + "time-cache: cache disabled", + "time_cache_init: hash_size < %d, using default, %d", + "time_cache_init: hash_size > %d, using default, %d", + "time_cache_init: cache_size < %d, using default, %d", + "time_cache_init: cache_size > %d, using default, %d", + "Error allocating memory for time_cache", + "Error allocating memory for time_cache entry", + "Error allocating memory for time_cache entry", + "Error inserting new time_cache entry", + "Error allocating memory for time_cache", + "cs-terminate failure (%s)", + "cs-init failure (%s)", + "cs-wait failure (%s)", + "cs-post failure (%s)", + "Unable to create nonblocking socket (%s)", + "error: could not set keepalive (%s)\n", + "error: could not set recv timeout (%s)\n", + "error: could not set send timeout (%s)\n", + "Unable to create nonblocking socket (%s)", + "sem_grab failed (%s)", + "sem_release failed (%s)", + "sem_release failed (%s)", + "Could not remove temporary directory %s, Error %d", + "Could not remove temporary directory %s, Error %d", + emptyString }; + +/* libraries in bucket for hashKey==27 */ +static struct DATABIN bucket27[] = { + {"base",base,205}, + {emptyString,NULL,0} }; + +/* strings in library cgiadmin */ +static char* cgiadminid[] = {"$DBT: cgiadmin in memory v1 $"}; +static char* cgiadmin[] = { + "", + "Missing REQUEST_METHOD", + "This should only be invoked as CGI program", + "Missing ADMSERV_ROOT", + "This should only be invoked as CGI program", + "Unrecognized request type.", + "Startup", + "Failed to initialize WinSock", + "Bad file", + "Expected servers.lst", + "Cluster: merge product.lst from %s", + "<i>Obtaining product information from %s://%s:%d</i><br>\n", + "Updated %s<BR>\n", + "Bad file", + "Expected servers-instance.lst", + "Warning: %s already exists, information not added<BR>\n", + "Error: failed to create file %s<BR>\n", + "Warning: directory %s will be removed because of above error<BR>\n", + "", + "Error: %s %s (errno = %d)<BR>\n", + "Error: %s %s, %s<BR>\n", + "ALLOW ALL OPERATIONS", + "ALLOW GET OPERATIONS", + "ALLOW SET OPERATIONS", + "You accessed this form with an invalid query string.", + "community has been removed.", + "Community Strings", + "Edit a Community", + "Add Another Community", + "", + "Edit", + "Remove", + "Do you really want to remove this entry?", + "<b>Community:</b> %s\n", + "<br><b>Operation:</b> %s\n", + "No communities exist.", + "You should enter a community string.", + "You should enter operation you want for this community.", + "the community entry has been changed", + "%s has been added", + "fails to start up master agent", + "Please refer to documents to start it", + "master agent start up", + "can't open config file", + "open temp file fails", + "open CONFIG fails", + "open CONFIG fails", + "No permission to start master agent", + "You must be running as super user. Please refer to documents", + "can't get tcp protocol entry\n", + "can't create a sockect", + "Please refer to documents for its configuration", + "a smux master agent is running or the smux port is not free yet", + "Please refer to documents for its configuration", + "can't get udp protocol entry\n", + "can't create a socket", + "Please refer to documents for its configuration", + "a snmpd is running or snmp port is not free yet", + "Please refer to documents for its configuration", + "can't get udp protocol entry\n", + "can't create a socket", + "Please refer to documents for its configuration", + "Fail to start", + "Please refer to documents for its configuration", + emptyString }; + +/* libraries in bucket for hashKey==28 */ +static struct DATABIN bucket28[] = { + {"cgiadmin",cgiadmin,63}, + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==29 */ +static struct DATABIN bucket29[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==30 */ +static struct DATABIN bucket30[] = { + {emptyString,NULL,0} }; + +/* libraries in bucket for hashKey==31 */ +static struct DATABIN bucket31[] = { + {emptyString,NULL,0} }; + +/* array of bucket pointers */ +static struct DATABIN* buckets[32] = { + bucket0, + bucket1, + bucket2, + bucket3, + bucket4, + bucket5, + bucket6, + bucket7, + bucket8, + bucket9, + bucket10, + bucket11, + bucket12, + bucket13, + bucket14, + bucket15, + bucket16, + bucket17, + bucket18, + bucket19, + bucket20, + bucket21, + bucket22, + bucket23, + bucket24, + bucket25, + bucket26, + bucket27, + bucket28, + bucket29, + bucket30, + bucket31 }; diff --git a/lib/libsi18n/getstrprop.c b/lib/libsi18n/getstrprop.c new file mode 100644 index 00000000..6b21fdd5 --- /dev/null +++ b/lib/libsi18n/getstrprop.c @@ -0,0 +1,137 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "i18n.h" + +#include "getstrmem.h" + +#include "libadminutil/resource.h" +#include "coreres.h" + +Resource *hResource = NULL; +char empty_string[] = ""; + +char* +XP_GetStringFromMemory(char* strLibraryName,int iToken); + + + +void +XP_InitStringDatabase(char* pathCWD, char* databaseName) +{ + hResource = core_res_init_resource (pathCWD, databaseName); +} + +char *XP_GetPropertyString(char* strLibraryName,int iToken, ACCEPT_LANGUAGE_LIST lang) +{ + char *key_name; + char *result = NULL; + + if (hResource == NULL) + return NULL; + + /*creating the key*/ + key_name=(char*)malloc(strlen(strLibraryName) + 10); + sprintf(key_name, "%s-%d", strLibraryName, iToken); + if(key_name == NULL) + return NULL; + + result = (char *) core_res_getstring(hResource, key_name, lang) ; + + if (key_name) + free (key_name); + + if (result == NULL) + return empty_string; + else + return result ; +} + +char* +XP_GetStringFromDatabase(char* strLibraryName, + char* strLanguage, + int key) +{ + char *result = NULL; + ACCEPT_LANGUAGE_LIST alanglist; + int n; + + /* + * display first choice language if available, otherwise + * use default which is english in most case + */ + if (hResource) { + n = XP_AccLangList (strLanguage, alanglist); + if (n >= MAX_ACCEPT_LANGUAGE) + alanglist[MAX_ACCEPT_LANGUAGE-1][0] = '\0'; + else + alanglist[n][0] = '\0'; + result = XP_GetPropertyString(strLibraryName, key, alanglist); + } + + /* we should never come here. */ + if (result == NULL) + result = XP_GetStringFromMemory(strLibraryName,key); + return result; +} + + +char* +XP_GetStringFromMemory(char* strLibraryName,int iToken) +{ + /* + * In memory model called by XP_GetStringFromDatabase + * does not use database (nsres, et al.). + * + * This function uses hash table for library lookup + * and direct lookup for string. + * + * This function is thread safe. + */ + + + unsigned hashKey; + int found = 0; + unsigned uToken = iToken; + char* cPtr; + DATABIN* pBucket; + + /* calculate hash key */ + hashKey = 0; + cPtr = strLibraryName; + while (*cPtr) { + hashKey += *(cPtr++); + } + hashKey &= BUCKET_MASK; + + /* get bucket for this hash key */ + pBucket = buckets[hashKey]; + + /* search overflow buckets */ + while (*(pBucket->pLibraryName)!='\0') { + if (strcmp(pBucket->pLibraryName,strLibraryName)==0) { + found = 1; + break; + } + pBucket++; + } + + if (!found) { + return emptyString; + } + + if (uToken<=pBucket->numberOfStringsInLibrary) { + return pBucket->pArrayOfLibraryStrings[uToken]; + } else { + /* string token out of range */ + return emptyString; + } + +} diff --git a/lib/libsi18n/gsslapd.h b/lib/libsi18n/gsslapd.h new file mode 100644 index 00000000..bbb44454 --- /dev/null +++ b/lib/libsi18n/gsslapd.h @@ -0,0 +1,37 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#define DATABASE_NAME "ns-slapd" + +#ifdef RESOURCE_STR + +#undef LIBRARY_NAME +#include "base/dbtbase.h" +#undef LIBRARY_NAME +#include "frame/dbtframe.h" +#undef LIBRARY_NAME +#include "httpdaemon/dbthttpdaemon.h" +#undef LIBRARY_NAME +#include "libaccess/dbtlibaccess.h" +#undef LIBRARY_NAME +#include "libadmin/dbtlibadmin.h" +#undef LIBRARY_NAME +#include "libir/dbtlibir.h" +#undef LIBRARY_NAME +#include "../ldap/clients/dsgw/dbtdsgw.h" + +static RESOURCE_GLOBAL allxpstr[] = { + base, + frame, + httpdaemon, + libaccess, + libadmin, + libir, + dsgw, + 0 +}; + +#endif /* ifdef RESOURCE_STR */ diff --git a/lib/libsi18n/makstrdb.c b/lib/libsi18n/makstrdb.c new file mode 100644 index 00000000..6f8b78dd --- /dev/null +++ b/lib/libsi18n/makstrdb.c @@ -0,0 +1,221 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "netsite.h" +#ifdef BERKELEY_DB_RESOURCE +#include "mcom_db.h" +#include "nsres.h" +#endif + +#define RESOURCE_STR + +/********************************************/ +/* Begin: Application dependent information */ +/********************************************/ + +#ifdef MCC_ADMSERV +#include "gsadmserv.h" +#define GSXXX_H_INCLUDED +#endif + +#ifdef NS_ENTERPRISE +#include "gshttpd.h" +#define GSXXX_H_INCLUDED +#endif + +#ifdef NS_DS +#include "gsslapd.h" +#define GSXXX_H_INCLUDED +#endif + +#ifdef NS_PERSONAL +#include "gshttpd.h" +#define GSXXX_H_INCLUDED +#endif + +#ifdef MCC_PROXY +#include "gsproxy.h" +#define GSXXX_H_INCLUDED +#endif + +#ifdef buildAnotherServer +#include "gsanother.h" +#define GSXXX_H_INCLUDED +#endif + +/********************************************/ +/* End: Application dependent information */ +/********************************************/ + +/**********************************************/ +/* Begin: Check that BUILD_MODULE is handled */ +/* and a gs*.h file has been included */ +/**********************************************/ + +#ifndef GSXXX_H_INCLUDED +#error Error in makstrdb.c: BUILD_MODULE not handled; gs*.h not included. +#endif + +/********************************************/ +/* End: Check that BUILD_MODULE is handled */ +/* and a gs*.h file has been included */ +/********************************************/ + +/*******************************************************************************/ + +#ifdef XP_DEBUG + +void +XP_PrintStringDatabase(void) /* debug routine */ +{ + int i; + int j; + char* LibraryName; + RESOURCE_TABLE* table; + + j = 0; + while (table=allxpstr[j++].restable) { + LibraryName = table->str; + printf("Library %d: %s\n",j,LibraryName); + i = 1; + table++; + while (table->str) { + printf("%d: %s %d \"%s\"\n",i,LibraryName,table->id,table->str); + i++; + table++; + } + } +} + +#endif /* XP_DEBUG */ + +#ifdef BERKELEY_DB_RESOURCE +/*******************************************************************************/ + +int +XP_MakeStringDatabase(void) +{ + int j; + char* LibraryName; + char* cptr; + RESOURCE_TABLE* table; + NSRESHANDLE hresdb; + char DBTlibraryName[128]; + + /* Creating database */ + hresdb = NSResCreateTable(DATABASE_NAME, NULL); + if (hresdb==0) { + printf("Error creating database %s\n",DATABASE_NAME); + return 1; + } + + j = 0; + while (table=allxpstr[j++].restable) { + LibraryName = table->str; + printf("Add Library %d: %s\n",j,LibraryName); + table++; + while (table->str) { + if (table->id==-1 && strstr(table->str,"$DBT: ")) { + cptr = strstr(table->str,"referenced"); + if (cptr) { + strncpy(cptr,"in DB file",10); + } + } + NSResAddString(hresdb,LibraryName,table->id,table->str,0); + table++; + } + } + + NSResCloseTable(hresdb); + return 0; +} +#endif + +/*******************************************************************************/ + +int +XP_MakeStringProperties(void) +{ + int j; + char* LibraryName; + char* cptr; + RESOURCE_TABLE* table; + FILE *hresfile; + char buffer[2000]; + char *src, *dest; + char *dbfile; + + /* Creating database */ + dbfile = (char *) malloc (strlen(DATABASE_NAME) + 20); + strcpy(dbfile, DATABASE_NAME); + strcat(dbfile, ".properties"); + + hresfile = fopen(dbfile, "w"); + + if (hresfile==NULL) { + printf("Error creating properties file %s\n",DATABASE_NAME); + return 1; + } + + j = 0; + while (table=allxpstr[j++].restable) { + LibraryName = table->str; + fprintf(hresfile, "\n"); + fprintf(hresfile, "#######################################\n"); + fprintf(hresfile, "############### %s ###############\n", LibraryName); + printf("Add Library %d: %s\n",j,LibraryName); + table++; + while (table->str) { + /* + Change special char to \uXXXX + */ + src = table->str; + dest = buffer; + while (*src) { + if (*src < 0x20) { + strcpy(dest,"\\u00"); + dest += 4; + sprintf(dest, "%02x", *src); + dest += 1; + } + else { + *dest = *src; + } + src ++; + dest ++; + } + *dest = '\0'; + + if (table->id > 0) { + fprintf(hresfile, "%s-%d =%s\n", LibraryName, table->id, buffer); + } + table++; + } + } + + fclose(hresfile); + return 0; +} + + + + +/*******************************************************************************/ + +int main() +{ +#if 0 + return XP_MakeStringDatabase(); +#else + return XP_MakeStringProperties(); +#endif +} + +/*******************************************************************************/ diff --git a/lib/libsi18n/propset.c b/lib/libsi18n/propset.c new file mode 100644 index 00000000..350bbe67 --- /dev/null +++ b/lib/libsi18n/propset.c @@ -0,0 +1,404 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include "i18n.h" + +#include "txtfile.h" +#include "reshash.h" +#include "propset.h" + +int PropertiesLoadFileToHash(PropertiesSet *propset, char *language); +char *GetProertiesFilename(char *directory, char *file, char *language); +int PropertiesLanguageStatus(PropertiesSet *propset, char *language); +int PropertiesSetLangStatus(LanguageStatus *langstatus, char *language, int status); +int unicode_to_UTF8(unsigned int wch, char *utf8); +char *decode_ascii(char *src); + + +PropertiesSet * PropertiesInit(char *directory, char *file) +{ + struct stat buf; + char * file_path; + PropertiesSet *propset = NULL; + PropertiesSet *result = NULL; + ResHash *reshash; + + file_path = (char *) malloc (strlen(directory) + strlen(file) + 20); + + strcpy(file_path, directory); + strcat(file_path, "/"); + strcat(file_path, file); + strcat(file_path, ".properties"); + + if (stat(file_path, &buf) == 0) { + propset = (PropertiesSet *) malloc(sizeof(PropertiesSet)); + memset(propset, 0, sizeof(PropertiesSet)); + reshash = (ResHash *) ResHashCreate(file); + + if (reshash) { + propset->langlist = (LanguageStatus *) malloc(sizeof(LanguageStatus)); + memset(propset->langlist, 0, sizeof(LanguageStatus)); + + propset->res = reshash; + propset->directory = strdup(directory); + propset->filename = strdup(file); + PropertiesLoadFileToHash(propset, NULL); + result = propset; + } + } + + if (file_path) + free (file_path); + + return result; +} + + +char *GetProertiesFilename(char *directory, char *file, char *language) +{ + char *filepath; + + if (language && *language == '\0') + filepath = (char *) malloc(strlen(directory) + strlen(file) + strlen(language) + 20); + else + filepath = (char *) malloc(strlen(directory) + strlen(file) + 20); + + strcpy(filepath, directory); + if (filepath[strlen(filepath) - 1] != '/') + strcat(filepath, "/"); + strcat(filepath, file); + if (language && *language != '\0') { + strcat(filepath, "_"); + strcat(filepath, language); + } + strcat(filepath, ".properties"); + + return filepath; +} + +/* + PropertiesLoadToHash + + Opens property file and save data to hash table + + Input + propfile: handle + file: full path with file extension + + return: + 0: SUCCESS + 1: FAIL +*/ + +int PropertiesLoadFileToHash(PropertiesSet *propset, char *language) +{ + TEXTFILE *hfile; + char *filepath; + char *p, *q; + int n; + char linebuf[1000]; + int st; + + st = PropertiesLanguageStatus(propset, language); + if (st == LANGUAGE_INVALID) + return 1; + else if (st == LANGUAGE_LOAD) + return 0; + + filepath = GetProertiesFilename(propset->directory, propset->filename, language); + + if ((hfile = OpenTextFile (filepath, TEXT_OPEN_FOR_READ)) == NULL) { + PropertiesSetLangStatus(propset->langlist, language, LANGUAGE_INVALID); + return 1; + } + + while ((n = ReadTextLine(hfile, linebuf)) >= 0) { + if (n == 0) + continue; + + p = linebuf; + /* strip leading spaces */ + while (*p == ' ' || *p == '\t') + p ++; + /* skip comment line */ + if (*p == '\0' || *p == '#' || *p == '=') + continue; + + q = strchr (linebuf, '='); + if (q) { + char *key, *value, *newvalue; + + *q = '\0'; + key = p; + value = q + 1; + /* strip trailing space for key */ + p = key + strlen(key) - 1; + while (*p == ' ' || *p == '\t') { + *p = '\0'; + p --; + } + + /* decode Unicode escape value */ + newvalue = decode_ascii(value); + + if (newvalue) { + ResHashAdd(propset->res, key, newvalue, language); + free(newvalue); + } + else + ResHashAdd(propset->res, key, value, language); + } + } + PropertiesSetLangStatus(propset->langlist, language, LANGUAGE_LOAD); + return 0; +} + +/* + PropertiesIsLoaded + + Test if current properties associated with language + is loaded or not. + + return: + 1: SUCCESS + 0: FAIL + */ + +int PropertiesLanguageStatus(PropertiesSet *propset, char *language) +{ + LanguageStatus *plang; + + plang = propset->langlist; + if (language == NULL || *language == '\0') { + return plang->status; + } + + plang = plang->next; + + while (plang) { + if (strcmp(plang->language, language) == 0) { + return plang->status; + } + plang = plang->next; + } + return LANGUAGE_NONE; +} + +int PropertiesSetLangStatus(LanguageStatus *langlist, char *language, int status) +{ + LanguageStatus *plang, *prev; + LanguageStatus *langstatus; + + if (language == NULL || *language == '\0') { + langlist->status = status; + return 0; + } + + prev = plang = langlist; + plang = plang->next; + + while (plang) { + if (strcmp(plang->language, language) == 0) { + plang->status = status; + return 0; + } + prev = plang; + plang = plang->next; + } + + langstatus = (LanguageStatus *) malloc(sizeof(LanguageStatus)); + memset (langstatus, 0, sizeof(LanguageStatus)); + langstatus->language = strdup(language); + langstatus->status = status; + prev->next = langstatus; + + return 0; +} + + +/*** + PropertiesOpenFile + + return 0: loaded + 1: fail to load file associated with the language + + + */ +int PropertiesOpenFile(PropertiesSet *propset, char *language) +{ + int status; + status = PropertiesLanguageStatus(propset, language); + + if (status == LANGUAGE_NONE) + return PropertiesLoadFileToHash (propset, language); + else if (status == LANGUAGE_INVALID) + return 1; + else + return 0; +} + +const char *PropertiesGetString(PropertiesSet *propset, char *key, ACCEPT_LANGUAGE_LIST acceptlangauge) +{ + int i; + char *language = NULL; + + i = 0; + while (acceptlangauge[i][0]) { + if (PropertiesOpenFile(propset, acceptlangauge[i]) == 0) { + language = acceptlangauge[i]; + break; + } + i ++; + } + + return ResHashSearch(propset->res, key, language); +} +void PropertiesDestroy(PropertiesSet *propset) +{ + LanguageStatus *langattrib, *next; + + if (propset) { + if (propset->path) + free(propset->path); + if (propset->directory) + free(propset->directory); + if (propset->filename) + free(propset->filename); + + ResHashDestroy(propset->res); + + langattrib = propset->langlist; + while (langattrib) { + next = langattrib->next; + if (langattrib->language) + free(langattrib->language); + free(langattrib); + langattrib = next; + } + } +} + + +char *decode_ascii(char *src) +{ + int i; + char utf8[10]; + int state = 0; + int digit = 0; + int digit_count = 0; + char *result, *p, *q; + + if (src == NULL || *src == '\0') + return NULL; + + if (strchr(src, '\\') == NULL) + return NULL; + + result = (char *) malloc(strlen(src) + 1); + + p = src; + q = result; + + for (;*p; p++) { + char ch; + int n; + if (state == BACKSLASH_U) { + ch = toupper(*p); + if (ch >= '0' && ch <= '9') { + digit = digit * 16 + (ch - '0'); + digit_count ++; + } + else if (ch >= 'A' && ch <= 'F') { + digit = digit * 16 + (ch - 'A' + 10); + digit_count ++; + } + else { + n = unicode_to_UTF8(digit, utf8); + for (i = 0; i < n; i++) + *q ++ = utf8[i]; + *q ++ = *p; + state = 0; + digit_count = 0; + } + + if (digit_count == 4) { + n = unicode_to_UTF8(digit, utf8); + for (i = 0; i < n; i++) + *q ++ = utf8[i]; + state = 0; + } + } + else if (state == BACKSLASH) { + if (*p == 'u') { + state = BACKSLASH_U; + digit = 0; + digit_count = 0; + continue; + } + else if (*p == 'n') { + *q++ = '\n'; + state = 0; + } + else if (*p == 'r') { + *q++ = '\r'; + state = 0; + } + else { + *q++ = '\\'; + *q++ = *p; + state = 0; + } + } + else if (*p == '\\') { + state = BACKSLASH; + continue; + } + else { + *q++ = *p; + state = 0; + } + } + *q = '\0'; + return result; +} + + +int unicode_to_UTF8(unsigned int wch, char *utf8) +{ + unsigned char hibyte, lobyte, mibyte; + + if (wch <= 0x7F) { + /* 0000 007F ==> 0xxxxxxx */ + utf8[0] = (unsigned char) wch ; + utf8[1] = '\0'; + return 1; + } + else if (wch <= 0x7FF) { + /* 0000 07FF ==> 110xxxxx 10xxxxxx */ + lobyte = wch & 0x3F; + hibyte = (wch >> 6) & 0x1F; + + utf8[0] = 0xC0 | hibyte; + utf8[1] = 0x80 | lobyte; + utf8[2] = '\0'; + return 2; + } + else { + /* FFFF ==> 1110xxxx 10xxxxxx 10xxxxxx */ + lobyte = wch & 0x3F; + mibyte = (wch >> 6) & 0x3F; + hibyte = (wch >> 12) & 0xF; + + utf8[0] = 0xE0 | hibyte; + utf8[1] = 0x80 | mibyte; + utf8[2] = 0x80 | lobyte; + utf8[3] = '\0'; + return 3; + } +} diff --git a/lib/libsi18n/propset.h b/lib/libsi18n/propset.h new file mode 100644 index 00000000..f7322669 --- /dev/null +++ b/lib/libsi18n/propset.h @@ -0,0 +1,43 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef PROPSET_H +#define PROPSET_H + +#include "reshash.h" + + +enum { + LANGUAGE_NONE = 0, + LANGUAGE_LOAD, + LANGUAGE_INVALID +} ; + +enum { + BACKSLASH = 1, + BACKSLASH_U +}; + + +typedef struct LanguageStatusS { + char *language; + int status; + struct LanguageStatusS *next; +} LanguageStatus; + +typedef struct PropertiesSet { + char *path; + char *directory; + char *filename; + LanguageStatus *langlist; + ResHash *res; +} PropertiesSet; + + +PropertiesSet * PropertiesInit(char *directory, char *file); +const char *PropertiesGetString(PropertiesSet *propset, char *key, ACCEPT_LANGUAGE_LIST acceptlangauge); +void PropertiesDestroy(PropertiesSet *propfile); + +#endif diff --git a/lib/libsi18n/reshash.c b/lib/libsi18n/reshash.c new file mode 100644 index 00000000..55740e19 --- /dev/null +++ b/lib/libsi18n/reshash.c @@ -0,0 +1,264 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "reshash.h" + +/* ======================== Value with Language list ==================== */ +int ValueAddLanguageItem(ValueNode *node, char *value, char *language) +{ + ValueNode *prev, *pvalue; + + if (node == NULL) + return 0; + if (language == NULL || *language == '\0') { + /* should be added to default value */ + return 0; + } + + prev = pvalue = node; + while (pvalue != NULL) { + if ((pvalue->language == NULL) || + (strcmp(pvalue->language,language) == 0)) { + /* if value for the language is already there + replace it with latest one. + */ + if (pvalue->language == NULL) + pvalue->language = strdup(language); + if (pvalue->value) + free(pvalue->value); + pvalue->value = strdup(value); + return 0; + } + prev = pvalue; + pvalue = pvalue->next; + } + pvalue = (ValueNode *) malloc(sizeof(ValueNode)); + memset(pvalue, 0, sizeof(ValueNode)); + + prev->next = pvalue; + + pvalue->language = strdup(language); + pvalue->value = strdup(value); + return 0; +} + +const char *ValueSearchItem(ValueNode *node, char *language) +{ + ValueNode *pvalue; + + if (node == NULL) + return NULL; + + pvalue = node; + while (pvalue && pvalue->language) { + if (strcmp(pvalue->language,language) == 0) { + return pvalue->value; + } + pvalue = pvalue->next; + } + return NULL; +} + +void ValueDestroy(ValueNode *node) +{ + ValueNode *p, *current; + p = node; + /* free itself and go next */ + while (p) { + current = p; + p = p->next; + if (current->language) + free (current->language); + if (current->value) + free (current->value); + } +} + +/* ======================== End of Value with Language list ==================== */ + + + +/* ======================== Tree List Implementation============================ */ + +const char * TreeSearchItem(TreeNode *res, char *key, char *language) +{ + int k; + const char *result; + + if (res == NULL || res->key == NULL) + return NULL; + + k = strcmp(key, res->key); + + if (k > 0) { + return TreeSearchItem(res->right, key, language); + } + else if (k < 0) { + return TreeSearchItem(res->left, key, language); + } + else { + /* Add to the current node; */ + if (language == NULL || *language == '\0') + return res->value; + + result = ValueSearchItem(res->vlist, language); + if (result) + return result; + else /* fallback to default value if there is any */ + return res->value; + } +} + +/* + TreeAddItem + Add value for specific language to the resource tree + + Using binary tree now --> Balanced tree later + */ +int TreeAddItem(TreeNode *res, char *key, char *value, char *language) +{ + TreeNode *node; + ValueNode *vnode; + int k; + + if (res->key == NULL) { + res->key = strdup(key); + k = 0; + } + else { + k = strcmp(key, res->key); + } + + if (k > 0) { + if (res->right == NULL) { + /* Create node and it's value sub list + */ + node = (TreeNode *) malloc (sizeof(TreeNode)); + memset(node, 0, sizeof(TreeNode)); + vnode = (ValueNode *) malloc(sizeof(ValueNode)); + memset(vnode, 0, sizeof(ValueNode)); + node->vlist = vnode; + + res->right = node; + + /* assign value to node */ + node->key = strdup(key); + if (language == NULL) + node->value = strdup(value); + else + ValueAddLanguageItem(node->vlist, value, language); + } + else { + return TreeAddItem(res->right, key, value, language); + } + } + else if (k < 0) { + if (res->left == NULL) { + node = (TreeNode *) malloc (sizeof(TreeNode)); + memset(node, 0, sizeof(TreeNode)); + vnode = (ValueNode *) malloc(sizeof(ValueNode)); + memset(vnode, 0, sizeof(ValueNode)); + node->vlist = vnode; + + res->left = node; + + /* assign value to node */ + node->key = strdup(key); + if (language == NULL) + node->value = strdup(value); + else + return ValueAddLanguageItem(node->vlist, value, language); + } + else { + return TreeAddItem(res->left, key, value, language); + } + } + else { + /* Add to the current node; */ + if (language == NULL) + res->value = strdup(value); + else + return ValueAddLanguageItem(res->vlist, value, language); + } + return 0; +} + +void TreeDestroy(TreeNode *tree) +{ + if (tree == NULL) + return; + if (tree->vlist) + ValueDestroy(tree->vlist); + if (tree->key) + free(tree->key); + if (tree->value) + free(tree->value); + if (tree->left) + TreeDestroy(tree->left); + if (tree->right) + TreeDestroy(tree->right); +} + +/* ====================== End of Tree implementation ================= */ + + +/* ====================== Tree controller (hash ?) ================ */ +ResHash * ResHashCreate(char * name) +{ + ResHash *pResHash; + + /* Create hash table */ + pResHash = (ResHash *) malloc (sizeof(ResHash)); + if (pResHash == NULL) + return NULL; + + memset(pResHash, 0, sizeof(ResHash)); + + if (name) + pResHash->name = strdup(name); + + /* Create initial tree item and it's valuelist to hash table */ + pResHash->treelist = (TreeNode *) malloc(sizeof(TreeNode)); + if (pResHash->treelist) + memset(pResHash->treelist, 0, sizeof(TreeNode)); + pResHash->treelist->vlist = (ValueNode *) malloc(sizeof(ValueNode)); + memset(pResHash->treelist->vlist, 0, sizeof(ValueNode)); + + return pResHash; +} + +int ResHashAdd(ResHash *res, char *key, char *value, char *language) +{ +#if 0 + hash = get hash value from key + tree = find the tree associated with hash value +#endif + return TreeAddItem(res->treelist, key, value, language); +} + +const char *ResHashSearch(ResHash *res, char *key, char *language) +{ +#if 0 + hash = get hash value from key + tree = find the tree associated with hash value +#endif + return TreeSearchItem(res->treelist, key, language); +} + +void ResHashDestroy(ResHash *res) +{ + if (res == NULL) + return; + if (res->name) + free(res->name); + if (res->treelist) + TreeDestroy(res->treelist); +} + +/* ========================= End of Tree controller ====================== */ diff --git a/lib/libsi18n/reshash.h b/lib/libsi18n/reshash.h new file mode 100644 index 00000000..b2b7d797 --- /dev/null +++ b/lib/libsi18n/reshash.h @@ -0,0 +1,54 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef RESHASH_H +#define RESHASH_H +/********************************************************************** + Hash --> Tree --> ValueList + + ValueList: language per item, each list associated with one key + Tree: contains multiple keys + Hash: Based on hash to decide withc tree to use for lookup + +***********************************************************************/ + +/* + Valuelist, each item contains + language: ISO two or four letters + value: UTF-8 encoding strings + */ +typedef struct ValueNode { + char *language; + char *value; + struct ValueNode *next; +} ValueNode; + + +/* + Current: BINARY TREE + Future: balanced tree for high search performance + */ +typedef struct TreeNodeStruct { + ValueNode *vlist; + char *key; + char *value; + struct TreeNodeStruct *left; + struct TreeNodeStruct *right; +} TreeNode; + + +typedef struct ResHash { + char *name; /* name of hash table */ + TreeNode *treelist; +} ResHash; + + +ResHash * ResHashCreate(char * name); +int ResHashAdd(ResHash *res, char *key, char *value, char *language); +const char *ResHashSearch(ResHash *res, char *key, char *language); +void ResHashDestroy(ResHash *res); + +#endif + diff --git a/lib/libsi18n/txtfile.c b/lib/libsi18n/txtfile.c new file mode 100644 index 00000000..a88d1672 --- /dev/null +++ b/lib/libsi18n/txtfile.c @@ -0,0 +1,130 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + + +#include "txtfile.h" + + + +#if 0 +char fileBuffer[FILE_BUFFER_SIZE + 1]; +char *fbCurrent; +int fbSize; +int fbStatus; +#endif + + +TEXTFILE * OpenTextFile(char *filename, int access) +{ + TEXTFILE *txtfile; + FILE *file; + int status; + + if (access == TEXT_OPEN_FOR_WRITE) { + status = TEXT_FILE_WRITING; + file = fopen(filename, "w+"); + } + else { + status = TEXT_FILE_READING; + file = fopen(filename, "r"); + } + + if (file == NULL) + return NULL; + + txtfile = (TEXTFILE *) malloc (sizeof(TEXTFILE)); + memset(txtfile, 0, sizeof(TEXTFILE)); + + txtfile->file = file; + txtfile->fbStatus = status; + + txtfile->fbCurrent = txtfile->fileBuffer; + *txtfile->fbCurrent = '\0'; + txtfile->fbSize = 0; + return txtfile; +} + + +void CloseTextFile(TEXTFILE *txtfile) +{ + if (txtfile) { + fclose(txtfile->file); + free(txtfile); + } + +} + +int FillTextBuffer(TEXTFILE *txtfile) +{ + int nLeft, size; + nLeft = strlen(txtfile->fbCurrent); + memcpy(txtfile->fileBuffer, txtfile->fbCurrent, nLeft+1); + + size = fread(txtfile->fileBuffer + nLeft, 1, FILE_BUFFER_SIZE - nLeft, txtfile->file); + if (size == 0) + return 0; + + txtfile->fbCurrent = txtfile->fileBuffer; + *(txtfile->fbCurrent + size + nLeft) = '\0'; + txtfile->fbSize = size + nLeft; + + return size; +} + +int ReadTextLine(TEXTFILE *txtfile, char *linebuf) +{ + char *p, *q; + + if (txtfile->fbStatus == TEXT_FILE_DONE) + return -1; + + p = txtfile->fbCurrent; + q = strchr(p, '\n'); + if (q) + { + *q = '\0'; + strcpy(linebuf, p); + txtfile->fbCurrent = q + 1; + return strlen(linebuf); + } + else + { + if (FillTextBuffer(txtfile) == 0) + { /* Done with file reading, + return last line + */ + txtfile->fbStatus = TEXT_FILE_DONE; + if (*txtfile->fbCurrent) { + strcpy(linebuf, txtfile->fbCurrent); + CloseTextFile(txtfile); + return strlen(linebuf); + } + else { + CloseTextFile(txtfile); + return -1; + } + } + else { + p = txtfile->fbCurrent; + q = strchr(p, '\n'); + if (q) + { + *q = '\0'; + strcpy(linebuf, p); + txtfile->fbCurrent = q + 1; + } + else + { + strcpy(linebuf, txtfile->fbCurrent); + txtfile->fbCurrent = txtfile->fbCurrent + strlen(linebuf); + } + } + return strlen(linebuf); + } +} diff --git a/lib/libsi18n/txtfile.h b/lib/libsi18n/txtfile.h new file mode 100644 index 00000000..d3f30755 --- /dev/null +++ b/lib/libsi18n/txtfile.h @@ -0,0 +1,45 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef TXTFILE_H +#define TXTFILE_H + +#define FILE_BUFFER_SIZE 2024 + +/* file status */ +enum { + TEXT_FILE_NONE, + TEXT_FILE_READING, + TEXT_FILE_WRITING, + TEXT_FILE_DONE +}; + +typedef struct TEXTFILE { + FILE *file; + char *fbCurrent; + int fbSize; + int fbStatus; + char fileBuffer[FILE_BUFFER_SIZE + 1]; +} TEXTFILE; + +enum { + TEXT_OPEN_FOR_READ, + TEXT_OPEN_FOR_WRITE +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +TEXTFILE * OpenTextFile(char *filename, int access); +void CloseTextFile(TEXTFILE *txtfile); +int ReadTextLine(TEXTFILE *txtfile, char *linebuf); + +#ifdef CPLUSPLUS +}; +#endif + +#endif |