From b2093e3016027d6b5cf06b3f91f30769bfc099e2 Mon Sep 17 00:00:00 2001 From: cvsadm Date: Fri, 21 Jan 2005 00:44:34 +0000 Subject: Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth) --- lib/base/Makefile | 111 ++ lib/base/crit.cpp | 399 +++++ lib/base/dns.cpp | 176 ++ lib/base/dnsdmain.cpp | 159 ++ lib/base/ereport.cpp | 249 +++ lib/base/eventlog.cpp | 70 + lib/base/file.cpp | 702 ++++++++ lib/base/fsmutex.cpp | 187 ++ lib/base/lexer.cpp | 978 +++++++++++ lib/base/lexer_pvt.h | 30 + lib/base/net.cpp | 578 +++++++ lib/base/nscperror.c | 162 ++ lib/base/nterrors.cpp | 83 + lib/base/plist.cpp | 1154 +++++++++++++ lib/base/plist_pvt.h | 122 ++ lib/base/pool.cpp | 654 +++++++ lib/base/rwlock.cpp | 131 ++ lib/base/shexp.cpp | 290 ++++ lib/base/shmem.cpp | 127 ++ lib/base/system.cpp | 264 +++ lib/base/systhr.cpp | 256 +++ lib/base/util.cpp | 1449 ++++++++++++++++ lib/ldaputil/Makefile | 65 + lib/ldaputil/cert.c | 452 +++++ lib/ldaputil/certmap.c | 1950 +++++++++++++++++++++ lib/ldaputil/certmap.conf | 48 + lib/ldaputil/dbconf.c | 704 ++++++++ lib/ldaputil/encode.c | 142 ++ lib/ldaputil/errors.c | 202 +++ lib/ldaputil/examples/Certmap.mak | 254 +++ lib/ldaputil/examples/Makefile | 91 + lib/ldaputil/examples/README | 97 ++ lib/ldaputil/examples/init.c | 40 + lib/ldaputil/examples/plugin.c | 239 +++ lib/ldaputil/examples/plugin.h | 33 + lib/ldaputil/init.c | 185 ++ lib/ldaputil/ldapauth.c | 1099 ++++++++++++ lib/ldaputil/ldapdb.c | 583 +++++++ lib/ldaputil/ldapu-changes.html | 403 +++++ lib/ldaputil/ldaputili.h | 62 + lib/ldaputil/utest/Makefile | 117 ++ lib/ldaputil/utest/auth.cpp | 574 ++++++ lib/ldaputil/utest/authtest | 106 ++ lib/ldaputil/utest/certmap.conf | 36 + lib/ldaputil/utest/dblist.conf | 15 + lib/ldaputil/utest/example.c | 116 ++ lib/ldaputil/utest/plugin.c | 115 ++ lib/ldaputil/utest/plugin.h | 20 + lib/ldaputil/utest/stubs.c | 107 ++ lib/ldaputil/utest/stubs.cpp | 102 ++ lib/ldaputil/utest/test.ref | 448 +++++ lib/ldaputil/vtable.c | 427 +++++ lib/libaccess/Makefile | 176 ++ lib/libaccess/access_plhash.cpp | 65 + lib/libaccess/access_plhash.h | 17 + lib/libaccess/acl.tab.cpp | 1718 ++++++++++++++++++ lib/libaccess/acl.tab.h | 44 + lib/libaccess/acl.yy.cpp | 1995 +++++++++++++++++++++ lib/libaccess/aclbuild.cpp | 1360 +++++++++++++++ lib/libaccess/aclcache.cpp | 579 +++++++ lib/libaccess/aclcache.h | 27 + lib/libaccess/aclerror.cpp | 246 +++ lib/libaccess/acleval.cpp | 556 ++++++ lib/libaccess/aclflush.cpp | 178 ++ lib/libaccess/aclparse.cpp | 2241 ++++++++++++++++++++++++ lib/libaccess/aclpriv.h | 171 ++ lib/libaccess/aclscan.h | 25 + lib/libaccess/aclscan.l | 379 ++++ lib/libaccess/aclspace.cpp | 37 + lib/libaccess/acltext.y | 957 ++++++++++ lib/libaccess/acltools.cpp | 3457 +++++++++++++++++++++++++++++++++++++ lib/libaccess/aclutil.cpp | 223 +++ lib/libaccess/aclutil.h | 25 + lib/libaccess/attrec.cpp | 309 ++++ lib/libaccess/authdb.cpp | 339 ++++ lib/libaccess/avadb.c | 300 ++++ lib/libaccess/avaparse.y | 140 ++ lib/libaccess/avapfile.c | 428 +++++ lib/libaccess/avascan.l | 106 ++ lib/libaccess/las.h | 53 + lib/libaccess/lasdns.cpp | 373 ++++ lib/libaccess/lasdns.h | 10 + lib/libaccess/lasgroup.cpp | 164 ++ lib/libaccess/lasip.cpp | 487 ++++++ lib/libaccess/lasip.h | 13 + lib/libaccess/lastod.cpp | 170 ++ lib/libaccess/lasuser.cpp | 154 ++ lib/libaccess/lcache.h | 23 + lib/libaccess/ldapacl.cpp | 822 +++++++++ lib/libaccess/ldapauth.h | 42 + lib/libaccess/leval.h | 18 + lib/libaccess/lparse.h | 27 + lib/libaccess/method.cpp | 163 ++ lib/libaccess/nsadb.cpp | 582 +++++++ lib/libaccess/nsamgmt.cpp | 1567 +++++++++++++++++ lib/libaccess/nsautherr.cpp | 126 ++ lib/libaccess/nscert.cpp | 963 +++++++++++ lib/libaccess/nsdb.cpp | 836 +++++++++ lib/libaccess/nsdbmgmt.cpp | 685 ++++++++ lib/libaccess/nseframe.cpp | 207 +++ lib/libaccess/nsgmgmt.cpp | 434 +++++ lib/libaccess/nsgroup.cpp | 336 ++++ lib/libaccess/nslock.cpp | 268 +++ lib/libaccess/nsumgmt.cpp | 456 +++++ lib/libaccess/nsuser.cpp | 309 ++++ lib/libaccess/oneeval.cpp | 1054 +++++++++++ lib/libaccess/oneeval.h | 17 + lib/libaccess/parse.h | 21 + lib/libaccess/permhash.h | 79 + lib/libaccess/register.cpp | 821 +++++++++ lib/libaccess/register.h | 98 ++ lib/libaccess/symbols.cpp | 350 ++++ lib/libaccess/userauth.cpp | 12 + lib/libaccess/usi.cpp | 371 ++++ lib/libaccess/usrcache.cpp | 657 +++++++ lib/libaccess/utest.mk | 61 + lib/libaccess/utest/.purify | 19 + lib/libaccess/utest/Makefile | 119 ++ lib/libaccess/utest/acl.dat | 12 + lib/libaccess/utest/aclfile0 | 55 + lib/libaccess/utest/aclfile1 | 11 + lib/libaccess/utest/aclfile10 | 13 + lib/libaccess/utest/aclfile11 | 11 + lib/libaccess/utest/aclfile12 | 11 + lib/libaccess/utest/aclfile13 | 11 + lib/libaccess/utest/aclfile14 | 11 + lib/libaccess/utest/aclfile15 | 11 + lib/libaccess/utest/aclfile16 | 11 + lib/libaccess/utest/aclfile17 | 11 + lib/libaccess/utest/aclfile18 | 19 + lib/libaccess/utest/aclfile19 | 14 + lib/libaccess/utest/aclfile2 | 11 + lib/libaccess/utest/aclfile3 | 11 + lib/libaccess/utest/aclfile4 | 11 + lib/libaccess/utest/aclfile5 | 11 + lib/libaccess/utest/aclfile6 | 23 + lib/libaccess/utest/aclfile7 | 11 + lib/libaccess/utest/aclfile8 | 11 + lib/libaccess/utest/aclfile9 | 11 + lib/libaccess/utest/aclgrp0 | 10 + lib/libaccess/utest/aclgrp1 | 10 + lib/libaccess/utest/aclgrp2 | 10 + lib/libaccess/utest/aclgrp3 | 10 + lib/libaccess/utest/aclgrp4 | 10 + lib/libaccess/utest/acltest.cpp | 796 +++++++++ lib/libaccess/utest/lasemail.cpp | 180 ++ lib/libaccess/utest/onetest.cpp | 47 + lib/libaccess/utest/shexp.cpp | 294 ++++ lib/libaccess/utest/shexp.h | 131 ++ lib/libaccess/utest/test.ref | 234 +++ lib/libaccess/utest/testmain.cpp | 52 + lib/libaccess/utest/twotest.cpp | 57 + lib/libaccess/utest/ustubs.cpp | 283 +++ lib/libaccess/winnt.l | 762 ++++++++ lib/libaccess/winnt.v | 156 ++ lib/libaccess/winnt.y | 793 +++++++++ lib/libaccess/wintab.h | 26 + lib/libaccess/yy-sed | 21 + lib/libadmin/Makefile | 61 + lib/libadmin/authdb.c | 2467 ++++++++++++++++++++++++++ lib/libadmin/error.c | 109 ++ lib/libadmin/strlist.c | 46 + lib/libadmin/template.c | 820 +++++++++ lib/libadmin/util.c | 1340 ++++++++++++++ lib/libnt/Makefile | 27 + lib/libnt/info.c | 81 + lib/libnt/path.c | 266 +++ lib/libnt/pmddeml.c | 375 ++++ lib/libnt/registry.c | 242 +++ lib/libnt/service.c | 375 ++++ lib/libnt/tcpip.c | 145 ++ lib/libsi18n/Makefile | 155 ++ lib/libsi18n/acclanglist.c | 187 ++ lib/libsi18n/coreres.c | 113 ++ lib/libsi18n/coreres.h | 15 + lib/libsi18n/getlang.c | 294 ++++ lib/libsi18n/getstrmem.c | 123 ++ lib/libsi18n/getstrmem.h | 1156 +++++++++++++ lib/libsi18n/getstrprop.c | 137 ++ lib/libsi18n/gsslapd.h | 37 + lib/libsi18n/makstrdb.c | 221 +++ lib/libsi18n/propset.c | 404 +++++ lib/libsi18n/propset.h | 43 + lib/libsi18n/reshash.c | 264 +++ lib/libsi18n/reshash.h | 54 + lib/libsi18n/txtfile.c | 130 ++ lib/libsi18n/txtfile.h | 45 + 187 files changed, 60143 insertions(+) create mode 100644 lib/base/Makefile create mode 100644 lib/base/crit.cpp create mode 100644 lib/base/dns.cpp create mode 100644 lib/base/dnsdmain.cpp create mode 100644 lib/base/ereport.cpp create mode 100644 lib/base/eventlog.cpp create mode 100644 lib/base/file.cpp create mode 100644 lib/base/fsmutex.cpp create mode 100644 lib/base/lexer.cpp create mode 100644 lib/base/lexer_pvt.h create mode 100644 lib/base/net.cpp create mode 100644 lib/base/nscperror.c create mode 100644 lib/base/nterrors.cpp create mode 100644 lib/base/plist.cpp create mode 100644 lib/base/plist_pvt.h create mode 100644 lib/base/pool.cpp create mode 100644 lib/base/rwlock.cpp create mode 100644 lib/base/shexp.cpp create mode 100644 lib/base/shmem.cpp create mode 100644 lib/base/system.cpp create mode 100644 lib/base/systhr.cpp create mode 100644 lib/base/util.cpp create mode 100644 lib/ldaputil/Makefile create mode 100644 lib/ldaputil/cert.c create mode 100644 lib/ldaputil/certmap.c create mode 100644 lib/ldaputil/certmap.conf create mode 100644 lib/ldaputil/dbconf.c create mode 100644 lib/ldaputil/encode.c create mode 100644 lib/ldaputil/errors.c create mode 100644 lib/ldaputil/examples/Certmap.mak create mode 100644 lib/ldaputil/examples/Makefile create mode 100644 lib/ldaputil/examples/README create mode 100644 lib/ldaputil/examples/init.c create mode 100644 lib/ldaputil/examples/plugin.c create mode 100644 lib/ldaputil/examples/plugin.h create mode 100644 lib/ldaputil/init.c create mode 100644 lib/ldaputil/ldapauth.c create mode 100644 lib/ldaputil/ldapdb.c create mode 100644 lib/ldaputil/ldapu-changes.html create mode 100644 lib/ldaputil/ldaputili.h create mode 100644 lib/ldaputil/utest/Makefile create mode 100644 lib/ldaputil/utest/auth.cpp create mode 100755 lib/ldaputil/utest/authtest create mode 100644 lib/ldaputil/utest/certmap.conf create mode 100644 lib/ldaputil/utest/dblist.conf create mode 100644 lib/ldaputil/utest/example.c create mode 100644 lib/ldaputil/utest/plugin.c create mode 100644 lib/ldaputil/utest/plugin.h create mode 100644 lib/ldaputil/utest/stubs.c create mode 100644 lib/ldaputil/utest/stubs.cpp create mode 100644 lib/ldaputil/utest/test.ref create mode 100644 lib/ldaputil/vtable.c create mode 100644 lib/libaccess/Makefile create mode 100644 lib/libaccess/access_plhash.cpp create mode 100644 lib/libaccess/access_plhash.h create mode 100644 lib/libaccess/acl.tab.cpp create mode 100644 lib/libaccess/acl.tab.h create mode 100644 lib/libaccess/acl.yy.cpp create mode 100644 lib/libaccess/aclbuild.cpp create mode 100644 lib/libaccess/aclcache.cpp create mode 100644 lib/libaccess/aclcache.h create mode 100644 lib/libaccess/aclerror.cpp create mode 100644 lib/libaccess/acleval.cpp create mode 100644 lib/libaccess/aclflush.cpp create mode 100644 lib/libaccess/aclparse.cpp create mode 100644 lib/libaccess/aclpriv.h create mode 100644 lib/libaccess/aclscan.h create mode 100644 lib/libaccess/aclscan.l create mode 100644 lib/libaccess/aclspace.cpp create mode 100644 lib/libaccess/acltext.y create mode 100644 lib/libaccess/acltools.cpp create mode 100644 lib/libaccess/aclutil.cpp create mode 100644 lib/libaccess/aclutil.h create mode 100644 lib/libaccess/attrec.cpp create mode 100644 lib/libaccess/authdb.cpp create mode 100644 lib/libaccess/avadb.c create mode 100644 lib/libaccess/avaparse.y create mode 100644 lib/libaccess/avapfile.c create mode 100644 lib/libaccess/avascan.l create mode 100644 lib/libaccess/las.h create mode 100644 lib/libaccess/lasdns.cpp create mode 100644 lib/libaccess/lasdns.h create mode 100644 lib/libaccess/lasgroup.cpp create mode 100644 lib/libaccess/lasip.cpp create mode 100644 lib/libaccess/lasip.h create mode 100644 lib/libaccess/lastod.cpp create mode 100644 lib/libaccess/lasuser.cpp create mode 100644 lib/libaccess/lcache.h create mode 100644 lib/libaccess/ldapacl.cpp create mode 100644 lib/libaccess/ldapauth.h create mode 100644 lib/libaccess/leval.h create mode 100644 lib/libaccess/lparse.h create mode 100644 lib/libaccess/method.cpp create mode 100644 lib/libaccess/nsadb.cpp create mode 100644 lib/libaccess/nsamgmt.cpp create mode 100644 lib/libaccess/nsautherr.cpp create mode 100644 lib/libaccess/nscert.cpp create mode 100644 lib/libaccess/nsdb.cpp create mode 100644 lib/libaccess/nsdbmgmt.cpp create mode 100644 lib/libaccess/nseframe.cpp create mode 100644 lib/libaccess/nsgmgmt.cpp create mode 100644 lib/libaccess/nsgroup.cpp create mode 100644 lib/libaccess/nslock.cpp create mode 100644 lib/libaccess/nsumgmt.cpp create mode 100644 lib/libaccess/nsuser.cpp create mode 100644 lib/libaccess/oneeval.cpp create mode 100644 lib/libaccess/oneeval.h create mode 100644 lib/libaccess/parse.h create mode 100644 lib/libaccess/permhash.h create mode 100644 lib/libaccess/register.cpp create mode 100644 lib/libaccess/register.h create mode 100644 lib/libaccess/symbols.cpp create mode 100644 lib/libaccess/userauth.cpp create mode 100644 lib/libaccess/usi.cpp create mode 100644 lib/libaccess/usrcache.cpp create mode 100644 lib/libaccess/utest.mk create mode 100644 lib/libaccess/utest/.purify create mode 100644 lib/libaccess/utest/Makefile create mode 100644 lib/libaccess/utest/acl.dat create mode 100644 lib/libaccess/utest/aclfile0 create mode 100644 lib/libaccess/utest/aclfile1 create mode 100644 lib/libaccess/utest/aclfile10 create mode 100644 lib/libaccess/utest/aclfile11 create mode 100644 lib/libaccess/utest/aclfile12 create mode 100644 lib/libaccess/utest/aclfile13 create mode 100644 lib/libaccess/utest/aclfile14 create mode 100644 lib/libaccess/utest/aclfile15 create mode 100644 lib/libaccess/utest/aclfile16 create mode 100644 lib/libaccess/utest/aclfile17 create mode 100644 lib/libaccess/utest/aclfile18 create mode 100644 lib/libaccess/utest/aclfile19 create mode 100644 lib/libaccess/utest/aclfile2 create mode 100644 lib/libaccess/utest/aclfile3 create mode 100644 lib/libaccess/utest/aclfile4 create mode 100644 lib/libaccess/utest/aclfile5 create mode 100644 lib/libaccess/utest/aclfile6 create mode 100644 lib/libaccess/utest/aclfile7 create mode 100644 lib/libaccess/utest/aclfile8 create mode 100644 lib/libaccess/utest/aclfile9 create mode 100644 lib/libaccess/utest/aclgrp0 create mode 100644 lib/libaccess/utest/aclgrp1 create mode 100644 lib/libaccess/utest/aclgrp2 create mode 100644 lib/libaccess/utest/aclgrp3 create mode 100644 lib/libaccess/utest/aclgrp4 create mode 100644 lib/libaccess/utest/acltest.cpp create mode 100644 lib/libaccess/utest/lasemail.cpp create mode 100644 lib/libaccess/utest/onetest.cpp create mode 100644 lib/libaccess/utest/shexp.cpp create mode 100644 lib/libaccess/utest/shexp.h create mode 100644 lib/libaccess/utest/test.ref create mode 100644 lib/libaccess/utest/testmain.cpp create mode 100644 lib/libaccess/utest/twotest.cpp create mode 100644 lib/libaccess/utest/ustubs.cpp create mode 100644 lib/libaccess/winnt.l create mode 100644 lib/libaccess/winnt.v create mode 100644 lib/libaccess/winnt.y create mode 100644 lib/libaccess/wintab.h create mode 100644 lib/libaccess/yy-sed create mode 100644 lib/libadmin/Makefile create mode 100644 lib/libadmin/authdb.c create mode 100644 lib/libadmin/error.c create mode 100644 lib/libadmin/strlist.c create mode 100644 lib/libadmin/template.c create mode 100644 lib/libadmin/util.c create mode 100644 lib/libnt/Makefile create mode 100644 lib/libnt/info.c create mode 100644 lib/libnt/path.c create mode 100644 lib/libnt/pmddeml.c create mode 100644 lib/libnt/registry.c create mode 100644 lib/libnt/service.c create mode 100644 lib/libnt/tcpip.c create mode 100644 lib/libsi18n/Makefile create mode 100644 lib/libsi18n/acclanglist.c create mode 100644 lib/libsi18n/coreres.c create mode 100644 lib/libsi18n/coreres.h create mode 100644 lib/libsi18n/getlang.c create mode 100644 lib/libsi18n/getstrmem.c create mode 100644 lib/libsi18n/getstrmem.h create mode 100644 lib/libsi18n/getstrprop.c create mode 100644 lib/libsi18n/gsslapd.h create mode 100644 lib/libsi18n/makstrdb.c create mode 100644 lib/libsi18n/propset.c create mode 100644 lib/libsi18n/propset.h create mode 100644 lib/libsi18n/reshash.c create mode 100644 lib/libsi18n/reshash.h create mode 100644 lib/libsi18n/txtfile.c create mode 100644 lib/libsi18n/txtfile.h (limited to 'lib') 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 +typedef sema_t counting_sem_t; +#elif defined(IRIX) && defined(HW_THREADS) +#include +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 /* inet_ntoa */ +#include /* 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 +#include +#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 +#include +#ifdef XP_UNIX +#include +#endif +#include +#include "util.h" + +/* Under NT, this is taken care of by net.h including winsock.h */ +#ifdef XP_UNIX +#include /* struct hostent */ +#endif +extern "C" { +#include +} +#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 +#include /* vsprintf */ +#include /* strcpy */ +#include /* localtime */ + +#ifdef XP_UNIX +#include /* error logging to syslog */ + +static SYS_FILE _error_fd; +#else /* WIN32 */ +#include +#include +#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 +#include +#include +#include "netsite.h" +#include "base/eventlog.h" +#include "frame/conf.h" +#include +#include + +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 +#include +#else +#include +#include +#endif +#ifdef XP_WIN32 +#include /* time */ +#include /* stat */ +#include +#include +#include +/* Removed for ns security integration +#include +*/ +#endif +#include + +#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 /* 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 +#include +#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 + +#include +/* 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 +#ifdef XP_UNIX +#include /* inet_ntoa */ +#include /* hostent stuff */ +#ifdef NEED_GHN_PROTO +extern "C" int gethostname (char *name, size_t namelen); +#endif +#endif /* XP_UNIX */ +#ifdef LINUX +#include /* ioctl */ +#endif + +extern "C" { +#include "ssl.h" +} + +#if defined(OSF1) +#include +#endif +#include "base/systems.h" +#include "base/dbtbase.h" + +#if defined(OSF1) +#include +#endif + +#ifdef IRIX +#include /* 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; indextv_sec)); + if (rv > 0) { + FD_ZERO(r); + FD_ZERO(w); + FD_ZERO(e); + for (index=0; index +#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 +#include +#include +#include +#include +#include +#include + +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 + +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, "

Memory pool status report

\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

\n"); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Freelist size: %d/%d

", freelist_size, + freelist_max); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Pool disabled: %d

", pool_disable); + net_write(sn->csd, tmp_buf, len); + + len = util_sprintf(tmp_buf, "Blocks created: %d

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, "

    \n"); + net_write(sn->csd, tmp_buf, len); + len = util_sprintf(tmp_buf, "\n"); + net_write(sn->csd, tmp_buf, len); + len = util_sprintf(tmp_buf, "\n"); + net_write(sn->csd, tmp_buf, len); +#ifdef DEBUG_CACHES + len = util_sprintf(tmp_buf, "\n"); + net_write(sn->csd, tmp_buf, len); +#endif + len = util_sprintf(tmp_buf, "\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 +"\n"); + } + } + ulsSortName(IndirectlyInList); + ulsGetCount(IndirectlyInList, &groupCount); + for (i=0; i\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; iuo_groups))){ + printf("\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("
    Pool #Pool size #Time CreatedBlocks
    %d %d \n", +#else +""); + + net_write(sn->csd, tmp_buf, len); + } + crit_exit(known_pools_lock); + + len = util_sprintf(tmp_buf, "
    Block #datacurr size max size
    %d %d %s \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, "\ + \ + \ + \ + \ + \ +\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, "\ + \ + \ + \ + \ + \ +\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, "
    Block #datacurr size max size
    %d%d%d%d
    %d%d%d%d
    \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 +#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 /* 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 +#include +#include +#include /* for nspr20 binary release */ + +NSPR_BEGIN_EXTERN_C +#include +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 +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 + +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 +#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 +#include +#include +#include "prthread.h" +#endif /* XP_UNIX */ + +#include "base/util.h" + +#include "base/dbtbase.h" +#include "base/ereport.h" + + +#ifdef XP_UNIX +#include +#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 /* 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 + * Sat, 17 Mar 1996 19:41:34 GMT + * + * 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 +#include +#include +#include +#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 +#include + +/* removed for ns security integration +#include +*/ +#include "prmem.h" +#include "key.h" +#include "cert.h" +#include +#include +#include +#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 , + 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 + * 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 +#include +#include +#include + +/* removed for ns security integration +#include +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#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 : "") + +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 +# : [] +# : [] +# +# 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 =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 +#default:InitFn 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 +#include +#include + +#include +#include +#include +#include + +#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: + * ":prop val" OR ":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 " */ + 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 +#include +#include +#include + +/* 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 +#include + +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 /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=, ou=, o=, c=". + +If the "E" attribute has a value, it returns a filter +"mail=". 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=". 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 /include. Copy the +../include/certmap.h file to the /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 +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 +#include +#include + +#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 +#include +#include + +#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=" between the + "CN" and the "O" attr-value pair. The 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 : ""); + + 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=" */ + 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 +#include +#include + +/*#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 /* for BUFSIZ */ +#include /* for strncpy, strcat */ +#include + +#include +#include +#include + +#include + +/* 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 + * if error, where 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 : ""); + 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 + * if error, where 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=) */ + 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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 + * if error, where 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=)(uniquemember=) ...)) + (& (objectclass=groupofnames) + (| (member=)(member=) ...))) + Construct 2 sub-filters first as follows: + (uniquemember=)(uniquemember=)... AND + (member=)(member=)... + 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 + * if error, where 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=)) + (& (objectclass=groupofnames)(member=))) + */ + + 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 + * if error, where 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 + * if error, where 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=) (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 + * if error, where 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 + * if error, where 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 +#include + +#ifdef LDAPDB_THREAD_SAFE +#include +#include +#include +/* removed for new ns security integration +#include +*/ +#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 +#include +#include "cert.h" +*/ +#include +#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 + * 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 + * 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 @@ + + + + + + + + + +
    +

    +Change Log for the ldaputil library

    + +
    +Author: Nitin More
    + +
    +E-mail:  nitin@netscape.com
    + +
    +Phone: (415) 937-4240
    + +
    + +
    +
    + +
    + +
    Changes since Apr 17, 1997
    + + +

    Last Update: Aug 25, 1997 + +

    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. +

    +Changed:

    +Several bug fixes went in since I last modified this file.  The important +ones are: +
      +
    • +79373: 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).
    • + +
    • +58474: If nested +group checks goes on for 30 recursions, a circular groups error is returned.
    • + +
    • +80004: after +thoroughly testing the certmap utility, several (mostly minor) oddities +were found & fixed.
    • +
    + +

    +Added:

    + +
      +
    • +79370: Group +membership check is optimized now through the new function ldapu_auth_userdn_groupids.  +Use this new function to get the optimization.  This function's algorithm +is as follows:
    • + +
      The first search uses the following filter (w/o the group names!): +
        (| (& (objectclass=groupofuniquenames) +
              (uniquemember=<userDN>)) +
           (& (objectclass=groupofnames)(member=<userDN>))) +
         
      +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: +
        (| (& (objectclass=groupofuniquenames) +
              (| (uniquemember=<grp1DN>)... +(uniquemember=<grpNDN>)) +
           (& (objectclass=groupofnames) +
              (| (member=<grp1DN>)... (member=<grpNDN>))
      +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. + +

      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! + +

      Caution: this function allows multiple groups +with the same "CN". + +

      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.

    + +
    Changes since Mar 22, 1997
    + + +

    Last Update: Apr 17, 1997 + +

    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). All 3.0 SuiteSpot +servers supporting client auth need to upgrade to this version. +

    +Changed:

    + +
      +
    • +Exchanged certmap.h & ldaputil.h: 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.
    • + +
    • +Renamed 'SearchComps' to 'FilterComps': Shouldn't affect your code +but may affect tests and documentation.
    • + +
    • +'VerifyCert' must be either "on" or "off": 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.
    • + +
    • +Important bug fixes: One bug was causing stack corruption & +weird unpredictable results. The other important bug was to map 'E' in +cert to 'MAIL' in LDAP.
    • +
    + +

    +Added:

    + +
      +
    • +Settable 'search function': User defined search function can be +set for cert to ldap mapping.
    • + +
    • +ldapu_get_cert_ava_val & ldapu_free_cert_ava_val: API functions +to get & free an attribute's value(s) from either the subject DN or +the issuer DN from a cert.
    • +
    + +

    +Open Bugs:

    + +
      Following bugs may not get fixed by RTM.
    + +
      +
    • +51279: 'uniquemember' +bug
    • + +
    • +58474:  +'circular groups' results in infinite loop
    • + +
    • +58478: Don't +allow a space as a valid delimeter for DNComps & FilterComps.
    • + +
    • +58482: Make +the 'search function' configurable.
    • + +
      +
    + +
    +

    +Changes since Mar 18, 1997

    + +
    +Last Update: Mar 22, 1997
    + +
    +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].
    + +

    +Changed:

    + +
      +
    • +ldapu_cert_to_ldap_entry: 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:
    • + +
        certmap verisign <verisign's DN> +
        verisign:dncomps +
        verisign:searchcomps cn, e
      +The mapped DN will be NULL so basedn will be used.  The filter will +be +
      (& (cn="<user's CN>") (mail="<user's mail>")).  The +new signature of ldapu_cert_to_ldap_entry is as follows: +
          int ldapu_cert_to_ldap_entry(void *cert, LDAP *ld, +const char *basedn, LDAPMessage **res); +
    • +verify cert functions: 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:
    • + +
          typedef int (*CertVerifyFn_t)(void *cert, LDAP *ld, +LDAPMessage *res, +
        + +

                                                             +LDAPMessage **entry); +

    • +typedef int (*CertMapInitiFn_t): 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:
    • + +
          typedef int (*CertMapInitFn_t)(void *certmap_info, +const char *issuerName, +
                                                                +const char *issuerDN); +
    • +(ldapauth.h) ldapu_auth_* functions: 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.
    • +
    + +
    + +
    + +
    +

    +Changes since Feb 1, 1997

    + +
    +Last Update: Mar 18, 1997
    + +
    +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.
    + +

    +Renamed:

    +Following structures and functions are renamed. But this shouldn't affect +you unless you have written a certmap plugin. +
    +struct CertMappingFunction_t ---> CertMapFn_t
    + +
    +struct CertVerifyFunction_t ---> CertVerifyFn_t
    + +
    +ldapu_set_cert_mapping_function ---> ldapu_set_cert_mapfn
    + +
    +ldapu_get_cert_mapping_function ---> ldapu_get_cert_mapfn
    + +
    +ldapu_set_cert_verify_function ---> ldapu_set_cert_verifyfn
    + +
    +ldapu_get_cert_verify_function ---> ldapu_get_cert_verifyfn
    + +

    +Removed: (from .h)

    +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. +
    +ldapu_cert_mapping_function_default
    + +
    +ldapu_cert_verify_function_default
    + +
    +ldapu_search_cert_dn
    + +
    +ldapu_subject_dn_to_ldap_dn
    + +

    +Changed:

    +The following changes may affect you. Please review them carefully before +you upgrade to the latest version of ldaputil. +
      +
    • +ldapu_auth_userdn_groupdn -- added const char *base argument +for group within group membership check
    • + +
    • +ldap_init and bind calls using LDAPDatabase_t *ldb retry once +if the LDAP server is/went down.
    • + +
    • +typedef CertVerifyFn_t has different arguments.
    • + +
    • +DNs from cert with escaped/quoted characters are correctly handled now.
    • + +
    • +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.
    • + +
    • +Only the required attrs are retrieved in most ldap_search_s calls +from ldaputil. Some new functions were added to keep older functions the +same.
    • + +
    • +Fixed a core dump in ldapu_free_LDAPDatabase_t when using the local db.
    • + +
    • +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.
    • +
    + +

    +Added:

    + +
      +
    • +Documentation to functions in ldaputil.h (customer API) - ldaputil.h is +the external public API for customers to write there plugins.
    • + +
    • +ldapu_get_cert_der - returns raw DER encoded cert data
    • + +
    • +ldapu_cert_to_user - Similar to ldapu_cert_to_ldap_entry but only +retrieves the 'uid' and 'userCertificate' attributes from LDAP and also +extracts the user id.
    • + +
    • +ldapu_find_uid_attrs and ldapu_find_group_attrs - 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.
    • + +
    • +ldapu_certinfo_delete, ldapu_certinfo_modify and ldapu_certinfo_save +- API for the certmap config file GUI tool which is not yet developed. +Any volunteers?
    • +
    + +

    +TODO/Bugs:

    + +
      +
    • +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:
    • + +
      +uniquemember: cn=Joe Smith,o=Netscape,c=US#jsmith
      + +
      +where, 'jsmith' is one of the values of the 'x500UniqueIdentifier' attribute +for the Joe Smith entry.
      + +
      +
      +
    + + + 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 + +#include + +#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 +#include +#include +#include + +#include // for PR_Init +#include // for PR_Exit +#include +#include +#include +#include +#include +#include +#include + +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 : "", s2 ? s2 : "", \ + 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 \n", argv[0]); + fprintf(stderr, "\t%s 2 \n", argv[0]); + fprintf(stderr, "\t%s 3 \n", argv[0]); + fprintf(stderr, "\t%s 4 \n", argv[0]); + fprintf(stderr, "\t%s 5 \n", argv[0]); + fprintf(stderr, "\t%s 6 \n", argv[0]); + fprintf(stderr, "\t%s 7 \n", argv[0]); + fprintf(stderr, "\t%s 8 \n", argv[0]); + fprintf(stderr, "\t%s 9 \n", argv[0]); + fprintf(stderr, "\t%s 10 \n", argv[0]); + fprintf(stderr, "\t%s 11 \n", argv[0]); + fprintf(stderr, "\t%s 12 ... to test save\n", argv[0]); + fprintf(stderr, "\t%s 13 ... to test delete\n", argv[0]); + fprintf(stderr, "\t%s 14 ... to test add\n", argv[0]); + fprintf(stderr, "\t%s 15 \n", argv[0]); + fprintf(stderr, "\t%s 16 \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 +must_succeed ./auth 1 "${USERDN}" "${GROUPDN}" +must_fail ./auth 1 "${USER2DN}" "${GROUPDN}" + +# test for +must_succeed ./auth 2 "${UID}" "${GROUPDN}" +must_fail ./auth 2 "${U2ID}" "${GROUPDN}" + +# test for +must_succeed ./auth 3 "${UID}" "${GROUPID}" +must_fail ./auth 3 "${U2ID}" "${GROUPID}" + +# test for +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 +must_succeed ./auth 5 "${USERDN}" "${ATTRFILTER}" +must_fail ./auth 5 "${USERDN}" "${ATTR2FILTER}" +must_fail ./auth 5 "${USER2DN}" "${ATTRFILTER}" + +# test for +must_succeed ./auth 6 "${UID}" "${ATTRFILTER}" +must_fail ./auth 6 "${UID}" "${ATTR2FILTER}" +must_fail ./auth 6 "${U2ID}" "${ATTRFILTER}" + +# test for +must_succeed ./auth 7 "${USERDN}" "hillock" +must_fail ./auth 7 "${USERDN}" "garbage" + +# test for +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 + +#include + +#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 +#include +#include + +#include /* must define extern "C" functions */ +#include /* 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 /* isspace */ +#include +#include /* sprintf */ +#include /* malloc */ + +#include +#include +#include +#include + +#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 /* isspace */ +#include +#include /* sprintf */ +#include /* malloc */ + +#include +#include +#include +#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 +#ifdef USE_LDAP_SSL +#include +#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 +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#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 +#include +#else +#include +#include +#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= 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 + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include + +/* 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 +#include +#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 +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#ifdef XP_WIN32 +#include +#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 +#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 +#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_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 +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#include +#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 +#include "libaccess/nserror.h" +#include "libaccess/nsautherr.h" +#include "libaccess/aclerror.h" +#include +#include + +#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 + +/* + * 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_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 +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include "aclcache.h" +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +#ifdef XP_UNIX +#include +#include /* ntohl */ +#include +#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 +#include +#include +#include +#include + + +#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 +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#ifdef XP_WIN32 +#include +#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} ; + +<> { + 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 +#include +#include "aclpriv.h" +#include + +/* 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 +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#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 ACL_QSTRING_TOK +%token ACL_READ_TOK +%token ACL_TERMINAL_TOK +%token ACL_VARIABLE_TOK +%token ACL_VERSION_TOK +%token ACL_WRITE_TOK +%token ACL_WITH_TOK + +%token ACL_EQ_TOK +%token ACL_GE_TOK +%token ACL_GT_TOK +%token ACL_LE_TOK +%token ACL_LT_TOK +%token 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($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, $1); + free($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, $1); + free($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($1); + strcpy(acl_tmp_arg, "http_"); + strcat(acl_tmp_arg, $1); + PERM_FREE($1); + acl_new_arg = PERM_STRDUP(acl_tmp_arg); + acl_add_arg(curr_args_list, acl_new_arg); + } else { + PERM_FREE($1); + } + } + | ACL_QSTRING_TOK + { + if (!use_generic_rights) { + acl_add_arg(curr_args_list, $1); + } else { + PERM_FREE($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, $1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_user_list, $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, $1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_ip_dns_list, $1); + } + ; + +ip_spec_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK + { + char tmp_str[255]; + + util_sprintf(tmp_str, "%s+%s", $1, $2); + free($1); + free($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($1); + if (strcmp($1, "database") == 0) { + free($1); + free($2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($1), $1, $2, NULL) < 0 ) { + } + free($1); + } + } + | ACL_VARIABLE_TOK ACL_QSTRING_TOK ';' + { + acl_string_lower($1); + if (strcmp($1, "database") == 0) { + free($1); + free($2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($1), $1, $2, NULL) < 0 ) { + } + free($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, $2); + free($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, $2); + free($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($1); + if ( ACL_ExprSetDenyWith(NULL, curr_expr, + $1, $3) < 0 ) { + yyerror("ACL_ExprSetDenyWith() failed"); + return(-1); + } + free($1); + free($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($1); + if ( ACL_ExprAddArg(NULL, curr_expr, $1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free($1); + } + ; + +parameter_list: parameter ';' + | parameter ';' parameter_list + ; + +parameter: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK + { + acl_string_lower($1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($1), $1, $3, NULL) < 0 ) { + } + free($1); + } + | ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK + { + acl_string_lower($1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($1), $1, $3, NULL) < 0 ) { + } + free($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($1); + if ( ACL_ExprAddArg(NULL, curr_expr, $1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free( $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($1); + if ( ACL_ExprTerm(NULL, curr_expr, + $1, (CmpOp_t) $2, $3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($1); + free($3); + return(-1); + } + free($1); + free($3); + } + | ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK + { + acl_string_lower($1); + if ( ACL_ExprTerm(NULL, curr_expr, + $1, (CmpOp_t) $2, $3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($1); + free($3); + return(-1); + } + free($1); + free($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 +#include + +#define ALLOCATE_ATTR_TABLE 1 /* Include the table of PList names */ + +#include +#include +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#include + +#include "aclscan.h" +#include "parse.h" +#include "oneeval.h" + +#include + +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 = ; + * 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 +#include +/* #include */ +#include +#include "aclpriv.h" +#include +#include +#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 +#include + +#include + +#include +#include "permhash.h" +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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 + + +#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 +#include +#include +#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 USER_ID DEF_ID + +%type 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 +#include +#include + +#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; inumOrgs; 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; inumOrgs; 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 ("\n"); + else + printf ("Insert an AVA-Database entry first\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 +#include +#include +#include +#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++;} +"*/" {num_nested_comments--; + if (!num_nested_comments) BEGIN old_state;} +. {;} + +{identifier} {yylval.string = PERM_STRDUP(yytext); + return USER_ID;} +":"{white_space}\{ {BEGIN DEF_TYPE; + old_state = DEF_TYPE;} + +"C" {BEGIN DEFINES; old_state = DEFINES; + return DEF_C; } +"O" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CO;} +"OU" {BEGIN DEFINES; old_state = DEFINES; + return DEF_OU;} +"CN" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CN;} +"L" {BEGIN DEFINES; old_state = DEFINES; + return DEF_L;} +"E" {BEGIN DEFINES; old_state = DEFINES; + return DEF_E;} +"ST" {BEGIN DEFINES; old_state = DEFINES; + return DEF_ST;} +"}" {BEGIN NORM;old_state = NORM;} + += {return EQ_SIGN;} +{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 +#include +#include + +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 +#include + +#ifdef XP_WIN32 +/* #include */ +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include "lasdns.h" +#include "aclutil.h" +#include "aclcache.h" +#include "permhash.h" +#include +#include +#include "access_plhash.h" +extern "C" { +#include +} + +#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 +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#include /* 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 +#include +#include +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include "lasip.h" +#include "aclutil.h" +#include "aclcache.h" +#include +#include + +#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< 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<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<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 + +#include +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include "aclutil.h" +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include "aclutil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#define __PRIVATE_NSADB +#include +#include +#include + +/* + * 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#define __PRIVATE_NSADB +#include +#include + +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 (.db) contains records indexed by a string + * key. These records contain the primary information in the + * database. A second DB file (.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 +#include +#include +#include +#define __PRIVATE_NSDB +#include + +#include + +#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 (.db) contains + * records indexed by a string key. These records contain the + * primary information in the database. A second DB file + * (.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 +#include +#include +#define __PRIVATE_NSDB +#include +#include + +/* + * 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 + +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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include +#include +#include +#include +#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; iabsolute) continue; // This right doesn't matter + for (j=0; jcount; 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; inext; + + 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_numabsolute) + continue; + + skipflag = 1; + + // Did this ace apply to this right? + for (i=0; icount; 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; idefault_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 +#include +#include +#include + +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 +#include +#include +#include +#include +#include "permhash.h" +#include +#include +#include "aclpriv.h" +#include +#include +#include +#include "aclcache.h" +#include +#include + +/* 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 +#include +#include + +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 +*/ +#include +#include +#include +#define __PRIVATE_SYMBOLS +#include "libaccess/symbols.h" +#include + +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 +extern "C" { +#include +} +#include +#include +#include +#include +#include +#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 : "", + 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 : "", + usrobj->uid, time); + } + else { + DBG_PRINT4("usr_cache expired: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "", + 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 : ""); + 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 : ""); + 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 : ""); + 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 : ""); + 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 +#include +#include +#include +#include +#include +#include +#include "../aclpriv.h" +#include +#include "../aclcache.h" +#include + + +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 +#include + +#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=) (mail=)) */ + 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=) (| (mail=) (mail=))) + */ + 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 + * /https-/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 +#include +#include +#include +#include +#include "../aclpriv.h" +#include +#include +#include +#include + +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 /* 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 /* isalnum */ +#include /* 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 +#include +#include +#include +#include "../aclpriv.h" +#include + +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 \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 +#include +#include +#include +#include +#include +#include +#include "../aclpriv.h" +#include +#include + + +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 +#include +#include +#include +#include + +#include +#include +#include "../aclpriv.h" +#include +#include +#include +#ifdef NSPR20 +#include +#else +#include +#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 +# 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 +#include +#include +#include +#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 +#include +#include +#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 +#include +#else +#include +#include +#endif + +#include + +#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= 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 /d +/#include /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 +#endif /* WIN32? */ + +#include +#include +#include +#include +#include +#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, ""); + } else { + fprintf(stdout, "No databases found."); + } +} + +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("%s", + 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
    "); + printf("Member of %s", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf(""); + sprintf(line, "remfromgrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Remove from Group", "grped", line); + printf(""); + printf("Indirect member of %s", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", group); + printf(""); + sprintf(line, "addtogrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Add to Group", "grped", line); + printf("
    "); + printf("Not a member of %s", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf(""); + sprintf(line, "addtogrp_but=1&memuser=%s&group=%s", + user, group); + output_cgi_link("Add to Group", "grped", line); + printf("
    \n"); + output_groups_user_is_in(db_path, user); + output_nonmembership(db_path, user); + printf("
    \n"); + printf("%s group membership:", user); + printf("
    \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("\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\n"); + } + } + printf("
    \n"); + printf("%s has these group members:", group); + printf("
    "); + printf("%s", memgroup); + sprintf(line, "group=%s", memgroup); + output_cgi_link("Edit Group", "grped", line); + printf(""); + sprintf(line, "remfromgrp_but=1&memgroup=%s&group=%s", + memgroup, group); + output_cgi_link("Remove from Group", "grped", line); + printf("
    \n"); + ulsFree(&sortList); + } else { + printf("This group has no group members."); + } + 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("\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\n"); + } + } + printf("
    \n"); + printf("%s has these user members:", group); + printf("
    "); + printf("%s", memuser); + sprintf(line, "user=%s", memuser); + output_cgi_link("Edit User", "usred", line); + printf(""); + sprintf(line, "remfromgrp_but=1&memuser=%s&group=%s", + memuser, group); + output_cgi_link("Remove from Group", "grped", line); + printf("
    \n"); + ulsFree(&sortList); + } else { + nsadbClose(padb, 0); + printf("This group has no user members."); + } + } + } + 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("\n"); + + for (i=0; i\n"); + } + } + printf("
    \n"); + printf("User List:"); + printf("
    "); + printf("%s", user); + sprintf(line, "user=%s", user); + output_cgi_link("Edit User", "usred", line); + printf("\n"); + output_cgi_link("Remove User", "usrrem", line); + printf("
    \n"); + } else { + printf("There are no users in the database."); + } + 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("\n"); + + for (i=0; i\n"); + } + } + printf("
    \n"); + printf("Group List:"); + printf("
    "); + printf("%s", group); + sprintf(line, "group=%s", group); + output_cgi_link("Edit Group", "grped", line); + printf(""); + output_cgi_link("Remove Group", "grprem", line); + printf("
    \n"); + } else { + printf("There are no groups in the database."); + } + 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("", name); + } + if((!multiple) && (none)) { + printf("