summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/acl
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/acl')
-rw-r--r--ldap/servers/plugins/acl/ACL-Notes215
-rw-r--r--ldap/servers/plugins/acl/Makefile96
-rw-r--r--ldap/servers/plugins/acl/acl.c4118
-rw-r--r--ldap/servers/plugins/acl/acl.h867
-rw-r--r--ldap/servers/plugins/acl/acl_ext.c968
-rw-r--r--ldap/servers/plugins/acl/aclanom.c536
-rw-r--r--ldap/servers/plugins/acl/acldllmain.c128
-rw-r--r--ldap/servers/plugins/acl/acleffectiverights.c674
-rw-r--r--ldap/servers/plugins/acl/aclgroup.c442
-rw-r--r--ldap/servers/plugins/acl/aclinit.c537
-rw-r--r--ldap/servers/plugins/acl/acllas.c3848
-rw-r--r--ldap/servers/plugins/acl/acllist.c940
-rw-r--r--ldap/servers/plugins/acl/aclparse.c1928
-rw-r--r--ldap/servers/plugins/acl/aclplugin.c355
-rw-r--r--ldap/servers/plugins/acl/aclproxy.c195
-rw-r--r--ldap/servers/plugins/acl/aclutil.c1475
-rw-r--r--ldap/servers/plugins/acl/libacl.def16
17 files changed, 17338 insertions, 0 deletions
diff --git a/ldap/servers/plugins/acl/ACL-Notes b/ldap/servers/plugins/acl/ACL-Notes
new file mode 100644
index 00000000..e275c967
--- /dev/null
+++ b/ldap/servers/plugins/acl/ACL-Notes
@@ -0,0 +1,215 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+
+Date What ?
+===================================
+10/15/98 - Created the ACL plugin
+ - Created a new file aclplugin.c and split the old
+ acl.c to acl.c & aclparse.c files.
+ - Merged changes made upt 4.0B2
+10/21/98 - Added USERATTR rule.
+
+
+02/01/99 - Cleanup needed to be done in 5.0 to make it a real plugin
+=====================================================================================
+1. Do not use slap.h but use slapi-plugin.h. This will require
+ some work. Work involves
+ 1) Making the ACLCB an extensible object of CONN struct
+ 2) Remove reference of Connection & operation struct
+ 3) Need slapi plugin apis to get the IP and DNS so that
+ we can evaluate it in the LASes.
+ 4) Need new option to get values of conn , op & pb stuct like
+ cert, authtype,
+
+2. Make ACLPB hang from the Operation struct instead of the PBlock.
+3. Make ACLCB an extensible object of CONN struct and remove any reference
+ about acl private info.
+
+4. I implemented the Userattr rule before even deciding if we need in 5.0
+ or not. I think it is useful. The documents those were based on are
+ in http://jazz/users/prasanta/acl_manage_filter
+
+5. Move acllas_dn_parent to the libslapd. This is duplicated code and is
+ BAAAD.
+
+6. Use the new normalized dn code so that we don't have to it over and over again.
+ We have to very careful ins slapi_access_allowed() as we keep the dn around and
+ free it later ( we can use dn by ref ).
+
+7. Merge from DS4.1 ( proxy auth) to DS 5.0.
+
+8. Miscs
+ a) can we use the SDK URL parsing code ?
+ b) Merge teh printing routines ( it's all over ).
+
+My estimate for doing the above cleanup will require anywhere between 5 to 8 days.
+Run the ACL tests after all the changes -- that is a MUST.
+===============================
+04/28/99
+
+ -- All the work descibed above is done.
+ -- Also
+ a) Created a Pool pf ACLPB one of which is grabed at the init time.
+ b) Created a global lockarary which takes care of the concurreny issue between
+ aclpb & aclcb
+ c) Fixed plugin init.
+
+
+I think the userattr rule should be made generic
+
+ useAttr = "attrName#Type"
+
+ <Type> :== DN | GROUP | ROLE | URL | <value>
+ <value> :== < any printable String>
+
+Example:
+ userAttr = "manager#DN" --- similar to userdnattr
+ userAttr = "owner#GROUP" --- similar to groupdnattr
+ userAttr = "attr#ROLE" --- The value of attr contains a role definition
+ userAttr = "myattr#URL" --- The value contains a URL or filter
+ userAttr = "OU#Directory Server"
+ --- In this case the client's OU and the
+ resource entry's OU must have
+ "Directory Server" value.
+
+ This way we can get rid of userdnattr and groupdnattr and accomplish a
+ lot with a single rule.
+
+At this point, we are done with the changes and waiting for what needs to be
+done in 5.0.
+=================================
+06/01/1999
+ -- Split the code into smaller modules
+ ( aclanom, aclgroup, aclinit, ...)
+ --- The ACLs are read and kept in a AVL tree.
+ --- Few bugs fixed in the acl_scan_match code.
+
+================================================
+07/02/99
+
+ -- Added support for parameterized bind rules.
+ -- Added support for caching of ATTR rules using recompute.S
+
+ What's left for 5.0
+ -------------------
+ 1. Support for roles
+ 2. Re-architect user/group cache
+ 3. startup in multiple threads ( low priority)
+ 4. look at add/delete/modrdn operations.
+ 5. cleanup:
+ - revist all the debug statements
+ - new tests etc.
+ 6. UI work
+
+============
+commit:14/12/99 rbyrne
+
+. Added targattrfilters keyword for value based acls.
+ Required also slapi_filter_apply(), slapi_get_attribute_type()
+ and slapi_attr_syntax_normalize() in slapd (filter.c and attrsyntax.c).
+. Memory leak fix in acl.c for PListInit() call--see comments in code.
+. made access an int on it's own to give room for expansion
+ (see aci_access and aclpb_access)
+. files: ACL-Notes, acl.c acl.h acl-ext.c aclanom.c acllas.c acllist.c aclparse.c aclutil.c slapd/attrsyntax.c slapd/slapi-plugin.h slapd/filter.c slapd/libslapd.def
+
+===
+commit: Mon 20th Dec 199
+. aclparse.c: add proxy back to acl_access2str
+. filter.c: get_filter() does not recurse anymore--get_fitler_internal(), get_filter_list()
+do the recursion...this way testing for ldapsubentry works.
+. aclinit.c: now have filter (|(aci=*)(objectclass=ldapsubentry)) in
+aclinit_search_and_insert_aci(). This means that when slapi_search_internal_callback()
+stops returning subentries by default, we will still get them as we have the correct filter.
+
+===
+commit: 12/01/2000:
+. aclplugin.c: fix for proxyauth bug in aclplugin_preop_search() and
+acl_plugin_preop_modify()--the proxy_dn and dn were swapped.
+. acl_ext.c: Also, when we PListAssignValue() on DS_ATTR_USERDN in acl_init_aclpb(),
+we should pass it a dn from aclpb_sdn, NOT the dn passed into acl_init_aclpb() which
+gets freed after the call to acl_init_acpb(). JAlso here need to be careful thatif dn contains NULL that we indicate this in aclpb_sdn by setting dn to a non-NULL empty string ("") which the code takes to be anon.
+. checked that none of the PList objects (DS_PROP_ACLPB, DS_ATTR_USERDN, DS_ATTR_ENTRY) have mem leak problems.
+. acl.c, acllas.c, aclproxy.c: removed some #ifdef 0 and comments--tidy up but
+no code changes.
+. acl_ext.c: in acl__done_aclpb() we need to PListDleteProp() on ACL_ATTR_IP
+and ACL_ATTR_DNS. This is because if LASIpEval/ACL_GetAttribute() and
+LASDnsEval/ACL_GetAttribute() see that these properties exist, they do
+not bother calling the respective Getter() function. So, everytime
+the aclpb is reused and ip or dns eval is required, the old value is used (
+or whatever hjappens to be in the memory.). Tested--works fine now with ip and dns keywords. ALso tested that when the same user tries an a non-allowed machine he is not allowed by accident (as he was before).
+. in schema.c/oc_find(): normalize the objectclass name before looking for it. Otherwise
+if there's a trailing space in the oc name, you won't dfind it.
+
+===
+commit:
+
+. aclparse.c: fix for syntax.ksh tp6 test: if there is no "version" in an aci item, reject it.
+. acllas.c: in DS_UserDnEval() now call slapi_normalize_dn() when comparing param strings and
+ ordinary dns.
+. acl_ext.c: when seeting DS_USER_DN_ATTR, get the ndn, the normalized form.
+
+====
+commit: 7/02/2000
+anom profile and groupdn != don't work together! Bug 381830 in 4.X
+. acl.h: new bit in aci_type to mark as below.
+. aclparse.c: mark an aci if it's like deny() groupdn != blah
+. aclanom.c: if marked like that cancel anom profile (just like userdn !=)
+==
+. removed these for the mo...
+commit:
+. acllas.c: now get the vattrs via slapi_vattr_merge_copy() when testing the client entry.
+. vattr.c: assign i the length of the list:i = type_context.list_length;
+. entry.c: slapi_entry_add_valueset()
+
+==
+
+commit: 03/03/2000
+. support for roledn in acis.
+===
+. acllist: in slapi_sdn_free(&aciListHead->acic_sdn); gbeelato's mem leak fix.
+commited
+
+=====
+
+committed: 17/008/00
+. support for $dn: aclutil.c, aclparse.c, acllist.c, acllas.c, acl.c, acl.h
+. acl_ext.c:Make sure aclpb_search_base is initialized to NULL in aclpb__malloc()
+. acl.c: set_result_status: wrong bit masks were being used in a_eval->attrEval_s_astatus etc.
+ acl__attr_cached_result(): in the attr==NULL case, need to test for potential
+"recompute" case of attribute--this happens if it's a param or attr style aci.
+
+========
+commited
+Support for dynamic backends:
+. acllist.c, aclinit.c, libslapd.def, control.c, slapi-plugin.h:
+ acl_be_state_change_fnc(), slapi_build_control_from_berval() etc.
+. aclanom.c: logical error in aclanom_match_profile() was causing misctest4 to fail.
+. acl_ext.c:fix mem leak by calling acl_clean_aclEval_control() in acl_ext_conn_desctructor()
+.
+===
+committed:24 Aug 2000
+now SLAPI_ACL_ALL (allow(all)) does NOT include proxy right
+
+==
+committed: 30 Aug 2000
+. acl.c: new print_access_control_Summary() routine to display final acl status. Gets the proxy
+ stuff right too.
+ in acl__resource_match_aci() always test the TARGET_FILTER case, the old cod ethere was wrong.
+==
+. add support for macros to userdn ldapurl keyword.
+
+
+==
+Committed:
+. Sep 07 2000: Support for $attr in macros.
+. Sep 15 2000: Support for aci macros in targetfilter keyword.
+. Sep 18 2000: improve ret code handling in __aclinit_handler--stops spurious error message.
+
+
+--eof
diff --git a/ldap/servers/plugins/acl/Makefile b/ldap/servers/plugins/acl/Makefile
new file mode 100644
index 00000000..bdfe2dc0
--- /dev/null
+++ b/ldap/servers/plugins/acl/Makefile
@@ -0,0 +1,96 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server acl-plugin.so acl plugins
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libacl
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+# ACL plugin depends on libadminutil
+MCC_INCLUDE += $(ADMINUTIL_INCLUDE)
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libacl.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(ACLINC)
+
+ACL_OBJS= acl.o acllas.o aclutil.o aclplugin.o aclparse.o acl_ext.o aclproxy.o \
+ aclinit.o aclgroup.o aclanom.o acllist.o acleffectiverights.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(ACL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBACL_DLL_OBJ = $(addprefix $(OBJDEST)/, acldllmain.o)
+endif
+
+LIBACL= $(addprefix $(LIBDIR)/, $(ACL_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(NSPRLINK) $(LDAP_LIBAVL) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+
+# ACL plugin depends on libadminutil (through libns-httpd)
+EXTRA_LIBS_DEP += $(NSHTTPD_DEP) $(ADMINUTIL_DEP) $(DBM_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(DBMLINK)
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBACCESS_DEP)
+EXTRA_LIBS += $(LIBACCESS)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libacl.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_LIBAVL) $(LDAP_SDK_LIBLDAP_DLL)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBACL)
+
+$(LIBACL): $(OBJS) $(LIBACL_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBACL_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBACL_DLL_OBJ)
+endif
+ $(RM) $(LIBACL)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/acl/acl.c b/ldap/servers/plugins/acl/acl.c
new file mode 100644
index 00000000..8dfbd52a
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl.c
@@ -0,0 +1,4118 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/****************************************************************************
+*
+* acl.c
+*
+*
+* This file contains the functions related to Access Control List (ACL)
+* checking. The ACL checking is based on the ONE ACL design implemented
+* in the Web server 2.0. For more information on the ACL design look
+* into the barracuda home page.
+*
+*
+******************************************************************************/
+
+
+/****************************************************************************/
+/* Globals. Must be protected by Mutex. */
+/****************************************************************************/
+/* Signatures to see if things have changed */
+static short acl_signature = 0;
+
+/****************************************************************************/
+/* Defines, Constants, ande Declarations */
+/****************************************************************************/
+static char *ds_map_generic[2] = { NULL, NULL };
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int acl__resource_match_aci(struct acl_pblock *aclpb, aci_t *aci ,
+ int skip_attrEval, int *a_matched);
+static acl__TestRights(Acl_PBlock *aclpb,int access, char **right,
+ char ** map_generic, aclResultReason_t *result_reason);
+static int acl__scan_for_acis(struct acl_pblock *aclpb, int *err);
+static void acl__reset_cached_result (struct acl_pblock *aclpb );
+static int acl__scan_match_handles ( struct acl_pblock *aclpb, int type);
+static int acl__attr_cached_result (struct acl_pblock *aclpb, char *attr, int access );
+static int acl__match_handlesFromCache (struct acl_pblock *aclpb, char *attr, int access);
+static int acl__get_attrEval ( struct acl_pblock *aclpb, char *attr );
+static int acl__config_get_readonly ();
+static int acl__recompute_acl (Acl_PBlock *aclpb, AclAttrEval *a_eval,
+ int access, int aciIndex);
+static void __acl_set_aclIndex_inResult ( Acl_PBlock *aclpb,
+ int access, int index );
+static int acl__make_filter_test_entry ( Slapi_Entry **entry,
+ char *attr_type, struct berval *attr_val);
+static int acl__test_filter ( Slapi_Entry *entry, struct slapi_filter *f,
+ int filter_sense);
+static void print_access_control_summary( char * source,
+ int ret_val, char *clientDn,
+ struct acl_pblock *aclpb,
+ char *right,
+ char *attr,
+ const char *edn,
+ aclResultReason_t *acl_reason);
+static int check_rdn_access( Slapi_PBlock *pb,Slapi_Entry *e, char * newrdn,
+ int access);
+
+
+/*
+ * Check the rdn permissions for this entry:
+ * require: write access to the entry, write (add) access to the new
+ * naming attribute, write (del) access to the old naming attribute if
+ * deleteoldrdn set.
+ *
+ * Valid only for the modrdn operation.
+*/
+int
+acl_access_allowed_modrdn(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* requested access rights */
+ )
+{
+ int retCode ;
+ char *newrdn, *oldrdn;
+ int deleteoldrdn = 0;
+
+ /*
+ * First check write permission on the entry--this is actually
+ * specially for modrdn.
+ */
+ retCode = acl_access_allowed ( pb, e, NULL /* attr */, NULL /* val */,
+ SLAPI_ACL_WRITE);
+
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to entry not allowed\n");
+ return(retCode);
+ }
+
+ /* Now get the new rdn attribute name and value */
+
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &oldrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+
+ /* Check can add the new naming attribute */
+ retCode = check_rdn_access( pb, e, newrdn, ACLPB_SLAPI_ACL_WRITE_ADD) ;
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to add new naming attribute not allowed\n");
+ return(retCode);
+ }
+
+ /* Check can delete the new naming attribute--if required */
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn );
+ if ( deleteoldrdn ) {
+ retCode = check_rdn_access( pb, e, oldrdn, ACLPB_SLAPI_ACL_WRITE_DEL) ;
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to delete old naming attribute not allowed\n");
+ return(retCode);
+ }
+ }
+
+ return(retCode);
+
+}
+/*
+ * Test if have access to make the first rdn of dn in entry e.
+*/
+
+static int check_rdn_access( Slapi_PBlock *pb, Slapi_Entry *e, char *dn,
+ int access) {
+
+ char **dns;
+ char **rdns;
+ int retCode = LDAP_INSUFFICIENT_ACCESS;
+ int i;
+
+ if ( (dns = ldap_explode_dn( dn, 0 )) != NULL ) {
+
+ if ( (rdns = ldap_explode_rdn( dns[0], 0 )) != NULL ) {
+
+ for ( i = 0; rdns[i] != NULL; i++ ) {
+ char *type;
+ struct berval bv;
+
+ if ( slapi_rdn2typeval( rdns[i], &type, &bv ) != 0 ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn: rdn2typeval (%s) failed\n",
+ escape_string( rdns[i], ebuf ));
+ retCode = LDAP_INSUFFICIENT_ACCESS;
+ break;
+ } else {
+ if ( (retCode = acl_access_allowed ( pb, e, type /* attr */,
+ &bv /* val */,
+ access)) != LDAP_SUCCESS) {
+ break;
+ }
+ }
+ }
+ ldap_value_free( rdns );
+ }
+ ldap_value_free( dns );
+ }
+
+ return(retCode);
+}
+
+/***************************************************************************
+*
+* acl_access_allowed
+* Determines if access to the resource is allowed or not.
+*
+* Input:
+* 
+*
+* Returns:
+*
+* Returns success/Denied/error condition
+*
+* LDAP_SUCCESS -- access allowed
+* LDAP_INSUFFICIENT_ACCESS -- access denied
+*
+* Errors returned:
+*
+* Some of the definition of the return values used copied from
+* "ldap.h" for convienience.
+* LDAP_OPERATIONS_ERROR
+* LDAP_PROTOCOL_ERROR
+* LDAP_UNWILLING_TO_PERFORM
+*
+*
+* Error Handling:
+* Returned error code.
+**************************************************************************/
+int
+acl_access_allowed(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* requested access rights */
+ )
+{
+ char *n_edn; /* Normalized DN of the entry */
+ int rv;
+ int err;
+ int ret_val;
+ char *right;
+ int num_handle;
+ struct acl_pblock *aclpb = NULL;
+ AclAttrEval *c_attrEval = NULL;
+ int got_reader_locked = 0;
+ int deallocate_attrEval = 0;
+ char ebuf [ BUFSIZ ];
+ char *clientDn;
+ Slapi_DN *e_sdn;
+ Slapi_Operation *op = NULL;
+ aclResultReason_t decision_reason;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op); /* for logging */
+
+ TNF_PROBE_1_DEBUG(acl_access_allowed_start,"ACL","",
+ tnf_int,access,access);
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ /**
+ * First, if the acl private write/delete on attribute right
+ * is requested, turn this into SLAPI_ACL_WRITE
+ * and record the original value.
+ * Need to make sure that these rights do not clash with the SLAPI
+ * public rights. This should be easy as the requested rights
+ * in the aclpb are stored in the bottom byte of aclpb_res_type,
+ * so if we keep the ACL private bits here too we make sure
+ * not to clash.
+ *
+ */
+
+ if ( access & (ACLPB_SLAPI_ACL_WRITE_ADD | ACLPB_SLAPI_ACL_WRITE_DEL) ) {
+ access |= SLAPI_ACL_WRITE;
+ }
+
+ n_edn = slapi_entry_get_ndn ( e );
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ /* Check if this is a write operation and the database is readonly */
+ /* No one, even the rootdn should be allowed to write to the database */
+ /* jcm: ReadOnly only applies to the public backends, the private ones */
+ /* (the DSEs) should still be writable for configuration. */
+ if ( access & ( SLAPI_ACL_WRITE | SLAPI_ACL_ADD | SLAPI_ACL_DELETE )) {
+ int be_readonly, privateBackend;
+ Slapi_Backend *be;
+
+ slapi_pblock_get ( pb, SLAPI_BE_READONLY, &be_readonly );
+ slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ privateBackend = slapi_be_private ( be );
+
+ if ( !privateBackend && (be_readonly || slapi_config_get_readonly () )){
+ slapi_log_error (loglevel, plugin_name,
+ "conn=%d op=%d (main): Deny %s on entry(%s)"
+ ": readonly backend\n",
+ op->o_connid, op->o_opid,
+ acl_access2str(access),
+ escape_string_with_punctuation(n_edn,ebuf));
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ /* Check for things we need to skip */
+ TNF_PROBE_0_DEBUG(acl_skipaccess_start,"ACL","");
+ if ( acl_skip_access_check ( pb, e )) {
+ slapi_log_error (loglevel, plugin_name,
+ "conn=%d op=%d (main): Allow %s on entry(%s)"
+ ": root user\n",
+ op->o_connid, op->o_opid,
+ acl_access2str(access),
+ escape_string_with_punctuation(n_edn,ebuf));
+ return(LDAP_SUCCESS);
+ }
+ TNF_PROBE_0_DEBUG(acl_skipaccess_end,"ACL","");
+
+
+ /* Get the bindDN */
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+
+ /* get the right acl pblock to work with */
+ if ( access & SLAPI_ACL_PROXY )
+ aclpb = acl_get_aclpb ( pb, ACLPB_PROXYDN_PBLOCK );
+ else
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 1 \n" );
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+ /* check if aclpb is initialized or not */
+ TNF_PROBE_0_DEBUG(acl_aclpbinit_start,"ACL","");
+ acl_init_aclpb ( pb, aclpb, clientDn, 0 );
+ TNF_PROBE_0_DEBUG(acl_aclpbinit_end,"ACL","");
+
+
+ /* Here we mean if "I am trying to add/delete "myself" ? " */
+ if (val && (access & SLAPI_ACL_WRITE) && (val->bv_len > 0) ) {
+ /* should use slapi_sdn_compare() but that'a an extra malloc/free */
+
+ char *dn_val_to_write =
+ slapi_dn_normalize(slapi_ch_strdup(val->bv_val));
+
+ if ( aclpb->aclpb_authorization_sdn &&
+ slapi_utf8casecmp((ACLUCHP)dn_val_to_write, (ACLUCHP)
+ slapi_sdn_get_ndn(aclpb->aclpb_authorization_sdn)) == 0) {
+ access |= SLAPI_ACL_SELF;
+ }
+
+ slapi_ch_free( (void **)&dn_val_to_write);
+ }
+
+ /* Convert access to string of rights eg SLAPI_ACL_ADD->"add". */
+ if ((right= acl_access2str(access)) == NULL) {
+ /* ERROR: unknown rights */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl_access_allowed unknown rights:%d\n", access);
+
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonymous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ TNF_PROBE_0_DEBUG(acl_anon_test_start,"ACL","");
+ if ( (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ )) &&
+ (clientDn && *clientDn == '\0')) {
+ aclanom_get_suffix_info(e, aclpb);
+ ret_val = aclanom_match_profile ( pb, aclpb, e, attr, access );
+ if (ret_val != -1 ) {
+ if (ret_val == LDAP_SUCCESS ) {
+ decision_reason.reason = ACL_REASON_ANON_ALLOWED;
+ } else if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ decision_reason.reason = ACL_REASON_ANON_DENIED;
+ }
+ goto cleanup_and_ret;
+ }
+ }
+ TNF_PROBE_0_DEBUG(acl_anon_test_end,"ACL","");
+
+ /* copy the value into the aclpb for later checking by the value acl code */
+
+ aclpb->aclpb_curr_attrVal = val;
+
+ if (!(aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST) &&
+ (access & SLAPI_ACL_SEARCH)) {
+ /* We are evaluating SEARCH right for the entry. After that
+ ** we will eval the READ right. We need to refresh the
+ ** list of acls selected for evaluation for the entry.
+ ** Initialize the array so that we indicate nothing has been
+ ** selected.
+ */
+ aclpb->aclpb_handles_index[0] = -1;
+ /* access is not allowed on entry for search -- it's for
+ ** read only.
+ */
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_ON_ENTRY;
+ }
+
+ /* set that this is a new entry */
+ aclpb->aclpb_res_type |= ACLPB_NEW_ENTRY;
+ aclpb->aclpb_access = 0;
+ aclpb->aclpb_access |= access;
+
+ /*
+ * stub the Slapi_Entry info first time and only it has changed
+ * or if the pblock is a psearch pblock--in this case the lifetime
+ * of entries associated with psearches is such that we cannot cache
+ * pointers to them--we must always start afresh (see psearch.c).
+ */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+ if ( operation_is_flag_set(op, OP_FLAG_PS) ||
+ (aclpb->aclpb_curr_entry_sdn == NULL) ||
+ (slapi_sdn_compare ( aclpb->aclpb_curr_entry_sdn, e_sdn) != 0)) {
+
+ TNF_PROBE_0_DEBUG(acl_entry_first_touch_start,"ACL","");
+
+ slapi_log_error(loglevel, plugin_name,
+ "#### conn=%d op=%d binddn=\"%s\"\n",
+ op->o_connid, op->o_opid, clientDn);
+ aclpb->aclpb_stat_total_entries++;
+
+ if (!(access & SLAPI_ACL_PROXY) &&
+ !( aclpb->aclpb_state & ACLPB_DONOT_EVALUATE_PROXY )) {
+ Acl_PBlock *proxy_pb;
+
+ proxy_pb = acl_get_aclpb( pb, ACLPB_PROXYDN_PBLOCK );
+ if (proxy_pb) {
+ TNF_PROBE_0_DEBUG(acl_access_allowed_proxy_start,"ACL","");
+ ret_val = acl_access_allowed( pb, e, attr, val, SLAPI_ACL_PROXY );
+ TNF_PROBE_0_DEBUG(acl_access_allowed_proxy_end,"ACL","");
+
+ if (ret_val != LDAP_SUCCESS) goto cleanup_and_ret;
+ }
+ }
+ if ( access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_num_entries++;
+
+ if ( aclpb->aclpb_num_entries == 1) {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ } else if ( aclpb->aclpb_state & ACLPB_COPY_EVALCONTEXT ) {
+ /* We need to copy the evalContext */
+ acl_copyEval_context ( aclpb, &aclpb->aclpb_curr_entryEval_context,
+ &aclpb->aclpb_prev_entryEval_context, 0 );
+ aclpb->aclpb_state &= ~ACLPB_COPY_EVALCONTEXT;
+ }
+ acl_clean_aclEval_context ( &aclpb->aclpb_curr_entryEval_context, 1 /*scrub */);
+ }
+
+ /* reset the cached result based on the scope */
+ acl__reset_cached_result (aclpb );
+
+ /* Find all the candidate aci's that apply by scanning up the DIT tree from edn. */
+
+ TNF_PROBE_0_DEBUG(acl_aciscan_start,"ACL","");
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+ slapi_sdn_set_dn_byval ( aclpb->aclpb_curr_entry_sdn, n_edn );
+ acllist_aciscan_update_scan ( aclpb, n_edn );
+ TNF_PROBE_0_DEBUG(acl_aciscan_end,"ACL","");
+
+ /* Keep the ptr to the current entry */
+ aclpb->aclpb_curr_entry = (Slapi_Entry *) e;
+
+ /* Get the attr info */
+ deallocate_attrEval = acl__get_attrEval ( aclpb, attr );
+
+ aclutil_print_resource ( aclpb, right, attr, clientDn );
+
+ /*
+ * Used to be PListInitProp(aclpb->aclpb_proplist, 0,
+ * DS_ATTR_ENTRY, e, 0);
+ *
+ * The difference is that PListInitProp() allocates a new property
+ * every time it's called, overwriting the old name in the PList hash
+ * table, but not freeing the original property.
+ * Now, we just create the property at aclpb_malloc() time and
+ * Assign a new value each time.
+ */
+
+ rv = PListAssignValue(aclpb->aclpb_proplist,
+ DS_ATTR_ENTRY, e, 0);
+
+ if (rv < 0) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Unable to set the Slapi_Entry in the Plist\n",0,0,0);
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+ TNF_PROBE_0_DEBUG(acl_entry_first_touch_end,"ACL","");
+
+ } else {
+ /* we are processing the same entry but for a different
+ ** attribute. If access is already allowed on that, entry, then
+ ** it's not a new entry anymore. It's the same old one.
+ */
+
+ TNF_PROBE_0_DEBUG(acl_entry_subs_touch_start,"ACL","");
+
+ aclpb->aclpb_res_type &= ~ACLPB_NEW_ENTRY;
+
+ /* Get the attr info */
+ deallocate_attrEval = acl__get_attrEval ( aclpb, attr );
+
+ TNF_PROBE_0_DEBUG(acl_entry_subs_touch_end,"ACL","");
+
+ }
+
+ /* get a lock for the reader */
+ acllist_acicache_READ_LOCK();
+ got_reader_locked = 1;
+
+ /*
+ ** Check if we can use any cached information to determine
+ ** access to this resource
+ */
+ if ( (access & SLAPI_ACL_SEARCH) &&
+ (ret_val = acl__match_handlesFromCache ( aclpb , attr, access)) != -1) {
+ /* means got a result: allowed or not*/
+
+ if (ret_val == LDAP_SUCCESS ) {
+ decision_reason.reason = ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ goto cleanup_and_ret;
+ }
+
+ /*
+ ** Now we have all the information about the resource. Now we need to
+ ** figure out if there are any ACLs which can be applied.
+ ** If no ACLs are there, then it's a DENY as default.
+ */
+ if (!(num_handle = acl__scan_for_acis(aclpb, &err))) {
+
+ /* We might have accessed the ACL first time which could
+ ** have caused syntax error.
+ */
+ if ( err == ACL_ONEACL_TEXT_ERR)
+ ret_val = LDAP_INVALID_SYNTAX;
+ else {
+ ret_val = LDAP_INSUFFICIENT_ACCESS;
+ decision_reason.reason = ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS;
+ }
+ goto cleanup_and_ret;
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Processed attr:%s for entry:%s\n", attr ? attr : "NULL",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION ( n_edn, ebuf), 0);
+
+ /*
+ ** Now evaluate the rights.
+ ** This is what we have been waiting for.
+ ** The return value should be ACL_RES_DENY or ACL_RES_ALLOW.
+ */
+ rv = acl__TestRights(aclpb, access, &right, ds_map_generic,
+ &decision_reason);
+ if ( rv != ACL_RES_ALLOW && (0 == strcasecmp ( right, "selfwrite") ) ) {
+ /* If I am adding myself to a group, we don't need selfwrite always,
+ ** write priv is good enough. Since libaccess doesn't provide me a nice
+ ** way to evaluate OR rights, I have to try again with wite priv.
+ ** bug: 339051
+ */
+ right = access_str_write;
+ rv = acl__TestRights(aclpb, access, &right, ds_map_generic,
+ &decision_reason);
+ }
+
+ if (rv == ACL_RES_ALLOW) {
+ ret_val = LDAP_SUCCESS;
+ } else {
+ ret_val = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+cleanup_and_ret:
+
+ TNF_PROBE_0_DEBUG(acl_cleanup_start,"ACL","");
+
+ /* I am ready to get out. */
+ if ( got_reader_locked ) acllist_acicache_READ_UNLOCK();
+
+ /* Store the status of the evaluation for this attr */
+ if ( aclpb && (c_attrEval = aclpb->aclpb_curr_attrEval )) {
+ if ( deallocate_attrEval ) {
+ /* In this case we are not caching the result as
+ ** we have too many attrs. we have malloced space.
+ ** Get rid of it.
+ */
+ slapi_ch_free ( (void **) &c_attrEval->attrEval_name );
+ slapi_ch_free ( (void **) &c_attrEval );
+ } else if (ret_val == LDAP_SUCCESS ) {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_status |= ACL_ATTREVAL_SUCCESS;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_SUCCESS;
+ else
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_INVALID;
+ } else {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_status |= ACL_ATTREVAL_FAIL;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_FAIL;
+ else
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_INVALID;
+ }
+ }
+
+ if ( aclpb ) aclpb->aclpb_curr_attrEval = NULL;
+
+ print_access_control_summary( "main", ret_val, clientDn, aclpb, right,
+ (attr ? attr : "NULL"),
+ escape_string_with_punctuation (n_edn, ebuf),
+ &decision_reason);
+ TNF_PROBE_0_DEBUG(acl_cleanup_end,"ACL","");
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_end,"ACL","");
+
+ return(ret_val);
+
+}
+
+static void print_access_control_summary( char *source, int ret_val, char *clientDn,
+ struct acl_pblock *aclpb,
+ char *right,
+ char *attr,
+ const char *edn,
+ aclResultReason_t *acl_reason)
+{
+ struct codebook {
+ int code;
+ char *text;
+ };
+
+ static struct codebook reasonbook[] = {
+ {ACL_REASON_NO_ALLOWS, "no allow acis"},
+ {ACL_REASON_RESULT_CACHED_DENY, "cached deny"},
+ {ACL_REASON_RESULT_CACHED_ALLOW, "cached allow"},
+ {ACL_REASON_EVALUATED_ALLOW, "allowed"},
+ {ACL_REASON_EVALUATED_DENY, "denied"},
+ {ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS, "no aci matched the resource"},
+ {ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS, "no aci matched the subject"},
+ {ACL_REASON_ANON_ALLOWED, "allow anyone aci matched anon user"},
+ {ACL_REASON_ANON_DENIED, "no matching anyone aci for anon user"},
+ {ACL_REASON_EVALCONTEXT_CACHED_ALLOW, "cached context/parent allow"},
+ {ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED, "cached context/parent deny"},
+ {ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW, "cached context/parent allow any attr"},
+ {ACL_REASON_NONE, "error occurred"},
+ };
+
+ char *anon = "anonymous";
+ char *null_user = "NULL"; /* bizare case */
+ char *real_user = NULL;
+ char *proxy_user = NULL;
+ char *access_allowed_string = "Allow";
+ char *access_not_allowed_string = "Deny";
+ char *access_error_string = "access_error";
+ char *access_status = NULL;
+ char *access_reason_none = "no reason available";
+ char *access_reason = access_reason_none;
+ char acl_info[ BUFSIZ ];
+ Slapi_Operation *op = NULL;
+ int loglevel;
+ int i;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ if ( !slapi_is_loglevel_set(loglevel) ) {
+ return;
+ }
+
+ slapi_pblock_get(aclpb->aclpb_pblock, SLAPI_OPERATION, &op); /* for logging */
+
+ if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ access_status = access_not_allowed_string;
+ } else if ( ret_val == LDAP_SUCCESS) {
+ access_status = access_allowed_string;
+ } else { /* some kind of error */
+ access_status = access_error_string;
+ }
+
+ /* decode the reason */
+ for (i = 0; i < sizeof(reasonbook) / sizeof(struct codebook); i++) {
+ if ( acl_reason->reason == reasonbook[i].code ) {
+ access_reason = reasonbook[i].text;
+ break;
+ }
+ }
+
+ /* get the acl */
+ acl_info[0] = '\0';
+ if (acl_reason->deciding_aci) {
+ if (acl_reason->reason == ACL_REASON_RESULT_CACHED_DENY ||
+ acl_reason->reason == ACL_REASON_RESULT_CACHED_ALLOW) {
+ /* acl is in cache. Its detail must have been printed before.
+ * So no need to print out acl detail this time.
+ */
+ PR_snprintf( &acl_info[0], BUFSIZ, "%s by aci(%d)",
+ access_reason,
+ acl_reason->deciding_aci->aci_index);
+ }
+ else {
+ PR_snprintf( &acl_info[0], BUFSIZ, "%s by aci(%d): aciname=%s, acidn=\"%s\"",
+ access_reason,
+ acl_reason->deciding_aci->aci_index,
+ acl_reason->deciding_aci->aclName,
+ slapi_sdn_get_ndn (acl_reason->deciding_aci->aci_sdn) );
+ }
+ }
+
+ /* Say who was denied access */
+
+ if (clientDn) {
+ if (clientDn[0] == '\0') {
+ /* anon */
+ real_user = anon;
+ } else {
+ real_user = clientDn;
+ }
+ } else {
+ real_user = null_user;
+ }
+
+ /* Is there a proxy */
+
+ if ( aclpb != NULL && aclpb->aclpb_proxy != NULL) {
+
+ if ( aclpb->aclpb_authorization_sdn != NULL ) {
+
+ proxy_user = (char *)(aclpb->aclpb_authorization_sdn->ndn ?
+ aclpb->aclpb_authorization_sdn->ndn:
+ null_user);
+
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s) to proxy (%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ proxy_user,
+ acl_info[0] ? acl_info : access_reason);
+ } else {
+ proxy_user = null_user;
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s) to proxy (%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ proxy_user,
+ acl_info[0] ? acl_info : access_reason);
+ }
+ } else{
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ acl_info[0] ? acl_info : access_reason);
+ }
+
+
+}
+/***************************************************************************
+*
+* acl_read_access_allowed_on_entry
+* check read access control on the given entry.
+*
+* Only used during seearch to test for read access on the entry.
+* (Could be generalized).
+*
+* attrs is the list of requested attributes passed with the search.
+* If the entry has no attributes (weird case) then the routine survives.
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - access allowed
+* LDAP_INSUFFICIENT_ACCESS - access denied
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_read_access_allowed_on_entry (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char **attrs,
+ int access /* access rights */
+ )
+{
+
+ struct acl_pblock *aclpb;
+ Slapi_Attr *currAttr;
+ Slapi_Attr *nextAttr;
+ int len;
+ int attr_index = -1;
+ char *attr_type = NULL;
+ int rv, isRoot;
+ char *clientDn;
+ unsigned long flags;
+ aclResultReason_t decision_reason;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_entry_start ,"ACL","");
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+
+ /*
+ ** If it's the root, or acl is off or the entry is a rootdse,
+ ** Then you have the privilege to read it.
+ */
+ if ( acl_skip_access_check ( pb, e ) ) {
+ char *n_edn = slapi_entry_get_ndn ( e );
+ char ebuf [ BUFSIZ ];
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Root access (%s) allowed on entry(%s)\n",
+ acl_access2str(access),
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf));
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,skip_access,"");
+ return LDAP_SUCCESS;
+ }
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 2 \n" );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,end,"aclpb error");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonympous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+ if ( clientDn && *clientDn == '\0' ) {
+ int ret_val;
+ ret_val = aclanom_match_profile ( pb, aclpb, e, NULL, SLAPI_ACL_READ );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,end,"anon");
+
+ if (ret_val != -1 ) return ret_val;
+ }
+
+ aclpb->aclpb_state &= ~ACLPB_RESET_MASK;
+ if (aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+ int ret_val;
+ ret_val = acl__attr_cached_result (aclpb, NULL, SLAPI_ACL_READ);
+ if (ret_val != -1 ) {
+ /* print summary if loglevel set */
+ if ( slapi_is_loglevel_set(loglevel) ) {
+ char *n_edn;
+ n_edn = slapi_entry_get_ndn ( e );
+ if ( ret_val == LDAP_SUCCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ /*
+ * pass NULL as the attr as this routine is concerned with
+ * access at the entry level.
+ */
+ print_access_control_summary( "on entry",
+ ret_val, clientDn, aclpb,
+ acl_access2str(SLAPI_ACL_READ),
+ NULL, n_edn,
+ &decision_reason);
+ }
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,eval_context_cached,"");
+
+ return ret_val;
+ }
+ }
+
+ /*
+ * Currently do not use this code--it results in confusing
+ * behaviour..see 529905
+ */
+#ifdef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+
+ /* Do we have access to the entry by virtue of
+ ** having access to an attr. Before that, let's find out which attrs
+ ** the user want. If the user has specified certain attributes, then
+ ** we check aginst that set of attributes.
+ */
+ if (!((aclpb->aclpb_state & ACLPB_USER_WANTS_ALL_ATTRS) ||
+ (aclpb->aclpb_state & ACLPB_USER_SPECIFIED_ATTARS))) {
+ int i;
+ if (attrs == NULL) {
+ aclpb->aclpb_state |= ACLPB_USER_WANTS_ALL_ATTRS;
+ } else {
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( strcmp( LDAP_ALL_USER_ATTRS, attrs[i] ) == 0 ) {
+ aclpb->aclpb_state |= ACLPB_USER_WANTS_ALL_ATTRS;
+ break;
+ }
+ }
+ }
+
+ if (!(aclpb->aclpb_state & ACLPB_USER_WANTS_ALL_ATTRS)) {
+ for (i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( !slapi_entry_attr_find ( e, attrs[i], &currAttr ) ) {
+ aclpb->aclpb_state |= ACLPB_USER_SPECIFIED_ATTARS;
+ break;
+ }
+ }
+ }
+ } /* end of all user test*/
+
+
+ /*
+ ** If user has specified a list of attrs, might as well use it
+ ** to determine access control.
+ */
+ currAttr = NULL;
+ attr_index = -1;
+ if ( aclpb->aclpb_state & ACLPB_USER_SPECIFIED_ATTARS) {
+ attr_index = 0;
+ attr_type = attrs[attr_index++];
+ } else {
+ /* Skip the operational attributes -- if there are any in the front */
+ slapi_entry_first_attr ( e, &currAttr );
+ if (currAttr != NULL) {
+ slapi_attr_get_flags ( currAttr, &flags );
+ while ( flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ flags = 0;
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( !rv ) slapi_attr_get_flags ( nextAttr, &flags );
+ currAttr = nextAttr;
+ }
+
+ /* Get the attr type */
+ if ( currAttr ) slapi_attr_get_type ( currAttr , &attr_type );
+ }
+ }
+
+#endif /*DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES*/
+
+#ifndef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+
+ /*
+ * Here look at each attribute in the entry and see if
+ * we have read access to it--if we do
+ * and we are not denied access to the entry then this
+ * is taken as implying access to the entry.
+ */
+ slapi_entry_first_attr ( e, &currAttr );
+ if (currAttr != NULL) {
+ slapi_attr_get_type ( currAttr , &attr_type );
+ }
+#endif
+ aclpb->aclpb_state |= ACLPB_EVALUATING_FIRST_ATTR;
+
+ while (attr_type) {
+ if (acl_access_allowed (pb, e,attr_type, NULL,
+ SLAPI_ACL_READ) == LDAP_SUCCESS) {
+ /*
+ ** We found a rule which requires us to test access
+ ** to the entry.
+ */
+ if ( aclpb->aclpb_state & ACLPB_FOUND_A_ENTRY_TEST_RULE){
+ /* Do I have access on the entry itself */
+ if (acl_access_allowed (pb, e, NULL,
+ NULL, access) != LDAP_SUCCESS) {
+ /* How was I denied ?
+ ** I could be denied on a DENY rule or because
+ ** there is no allow rule. If it's a DENY from
+ ** a DENY rule, then we don't have access to
+ ** the entry ( nice trick to get in )
+ */
+ if ( aclpb->aclpb_state &
+ ACLPB_EXECUTING_DENY_HANDLES)
+ return LDAP_INSUFFICIENT_ACCESS;
+
+ /* The other case is I don't have an
+ ** explicit allow rule -- which is fine.
+ ** Since, I am already here, it means that I have
+ ** an implicit allow to the entry.
+ */
+ }
+ }
+ aclpb->aclpb_state &= ~ACLPB_EVALUATING_FIRST_ATTR;
+
+ /*
+ ** If we are not sending all the attrs, then we must
+ ** make sure that we have right on a attr that we are
+ ** sending
+ */
+ len = strlen(attr_type);
+ if ( len > ACLPB_MAX_ATTR_LEN) {
+ slapi_ch_free ( (void **) &aclpb->aclpb_Evalattr);
+ aclpb->aclpb_Evalattr = slapi_ch_malloc(len);
+ }
+ strncpy (aclpb->aclpb_Evalattr, attr_type, len);
+ aclpb->aclpb_Evalattr[len] = '\0';
+ if ( attr_index >= 0 ) {
+ /*
+ * access was granted to one of the user specified attributes
+ * which was found in the entry and that attribute is
+ * now in aclpb_Evalattr
+ */
+ aclpb->aclpb_state |=
+ ACLPB_ACCESS_ALLOWED_USERATTR;
+ } else {
+ /*
+ * Access was granted to _an_ attribute in the entry and that
+ * attribute is now in aclpb_Evalattr
+ */
+ aclpb->aclpb_state |=
+ ACLPB_ACCESS_ALLOWED_ON_A_ATTR;
+ }
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end , "ACL","",
+ tnf_string,called_access_allowed,"");
+
+ return LDAP_SUCCESS;
+ } else {
+ /* try the next one */
+ attr_type = NULL;
+ if (attr_index >= 0) {
+ attr_type = attrs[attr_index++];
+ } else {
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( rv != 0 ) break;
+ currAttr = nextAttr;
+ slapi_attr_get_flags ( currAttr, &flags );
+ while ( flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ flags = 0;
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( !rv ) slapi_attr_get_flags ( nextAttr, &flags );
+ currAttr = nextAttr;
+ }
+ /* Get the attr type */
+ if ( currAttr ) slapi_attr_get_type ( currAttr , &attr_type );
+ }
+ }
+ }
+
+ /*
+ ** That means. we have searched thru all the attrs and found
+ ** access is denied on all attrs.
+ **
+ ** If there were no attributes in the entry at all (can have
+ ** such entries thrown up by the b/e, then we do
+ ** not have such an implied access.
+ */
+ aclpb->aclpb_state |= ACLPB_ACCESS_DENIED_ON_ALL_ATTRS;
+ aclpb->aclpb_state &= ~ACLPB_EVALUATING_FIRST_ATTR;
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","");
+
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+
+/***************************************************************************
+*
+* acl_read_access_allowed_on_attr
+* check access control on the given attr.
+*
+* Only used during search to test for read access to an attr.
+* (Could be generalized)
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - access allowed
+* LDAP_INSUFFICIENT_ACCESS - access denied
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_read_access_allowed_on_attr (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* access rights */
+ )
+{
+
+ struct acl_pblock *aclpb = NULL;
+ char ebuf [ BUFSIZ ];
+ char *clientDn = NULL;
+ char *n_edn;
+ aclResultReason_t decision_reason;
+ int ret_val = -1;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_attr_start ,"ACL","");
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ /* I am here, because I have access to the entry */
+
+ n_edn = slapi_entry_get_ndn ( e );
+
+ /* If it's the root or acl is off or rootdse, he has all the priv */
+ if ( acl_skip_access_check ( pb, e ) ) {
+ char ebuf [ BUFSIZ ];
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Root access (%s) allowed on entry(%s)\n",
+ acl_access2str(access),
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), 0);
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,skip_aclcheck,"");
+
+ return LDAP_SUCCESS;
+ }
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 3 \n" );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclpb_error,"");
+
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonympous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ slapi_pblock_get (pb, SLAPI_REQUESTOR_DN ,&clientDn );
+ if ( clientDn && *clientDn == '\0' ) {
+ ret_val = aclanom_match_profile ( pb, aclpb, e, attr,
+ SLAPI_ACL_READ );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,anon_decision,"");
+ if (ret_val != -1 ) return ret_val;
+ }
+
+ /* Then I must have a access to the entry. */
+ aclpb->aclpb_state |= ACLPB_ACCESS_ALLOWED_ON_ENTRY;
+
+ if ( aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+
+ ret_val = acl__attr_cached_result (aclpb, attr, SLAPI_ACL_READ);
+ if (ret_val != -1 ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "MATCHED HANDLE:dn:%s attr: %s val:%d\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), attr,
+ ret_val );
+ if ( ret_val == LDAP_SUCCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ goto acl_access_allowed_on_attr_Exit;
+ } else {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ }
+ }
+
+ if (aclpb->aclpb_state & ACLPB_ACCESS_DENIED_ON_ALL_ATTRS) {
+ /* access is denied on all the attributes */
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,deny_all_attrs,"");
+
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* do I have access to all the entries by virtue of having aci
+ ** rules with targetattr ="*". If yes, then allow access to
+ ** rest of the attributes.
+ */
+ if (aclpb->aclpb_state & ACLPB_ATTR_STAR_MATCHED) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "STAR Access allowed on attr:%s; entry:%s \n",
+ attr, ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), 0);
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW;
+ ret_val = LDAP_SUCCESS;
+ goto acl_access_allowed_on_attr_Exit;
+
+ }
+
+ if (aclpb->aclpb_state & ACLPB_ACCESS_ALLOWED_ON_A_ATTR) {
+
+ /* access is allowed on that attr.
+ ** for example: Slapi_Entry: cn, sn. phone, uid, passwd, address
+ ** We found that access is allowed on phone. That means the
+ ** -- access is denied on cn, sn
+ ** -- access is allowed on phone
+ ** -- Don't know about the rest. Need to evaluate.
+ */
+
+ if ( slapi_attr_type_cmp (attr, aclpb->aclpb_Evalattr, 1) == 0) {
+ /* from now on we need to evaluate access on
+ ** rest of the attrs.
+ */
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_ON_A_ATTR;
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclp_Evalattr1,"");
+
+ return LDAP_SUCCESS;
+ } else {
+ /*
+ * Here, the attr that implied access to the entry (aclpb_Evalattr),
+ * is not
+ * the one we currently want evaluated--so
+ * we need to evaluate access to attr--so fall through.
+ */
+ }
+
+ } else if (aclpb->aclpb_state & ACLPB_ACCESS_ALLOWED_USERATTR) {
+ /* Only skip evaluation on the user attr on which we have
+ ** evaluated before.
+ */
+ if ( slapi_attr_type_cmp (attr, aclpb->aclpb_Evalattr, 1) == 0) {
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_USERATTR;
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclp_Evalattr2,"");
+ return LDAP_SUCCESS;
+ }
+ }
+
+ /* we need to evaluate the access on this attr */
+ return ( acl_access_allowed(pb, e, attr, val, access) );
+
+ /* This exit point prints a summary and returns ret_val */
+acl_access_allowed_on_attr_Exit:
+
+ /* print summary if loglevel set */
+ if ( slapi_is_loglevel_set(loglevel) ) {
+
+ print_access_control_summary( "on attr",
+ ret_val, clientDn, aclpb,
+ acl_access2str(SLAPI_ACL_READ),
+ attr, n_edn, &decision_reason);
+ }
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","");
+
+ return(ret_val);
+}
+/***************************************************************************
+*
+* acl_check_mods
+* check access control on the given entry to see if
+* it allows the given modifications by the user associated with op.
+*
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - mods allowed ok
+* <err> - same return value as acl_access_allowed()
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_check_mods(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPMod **mods,
+ char **errbuf
+)
+{
+ int i;
+ int rv, accessCheckDisabled;
+ int lastmod = 0;
+ Slapi_Attr *attr = NULL;
+ char *n_edn;
+ Slapi_Backend *be = NULL;
+ Slapi_DN *e_sdn;
+ Acl_PBlock *aclpb = acl_get_aclpb ( pb, ACLPB_PROXYDN_PBLOCK );
+ LDAPMod *mod;
+ Slapi_Mods smods;
+
+ rv = slapi_pblock_get ( pb, SLAPI_PLUGIN_DB_NO_ACL, &accessCheckDisabled );
+ if ( rv != -1 && accessCheckDisabled ) return LDAP_SUCCESS;
+
+ if ( NULL == aclpb )
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ n_edn = slapi_entry_get_ndn ( e );
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ slapi_mods_init_byref(&smods,mods);
+
+ for (mod = slapi_mods_get_first_mod(&smods);
+ mod != NULL;
+ mod = slapi_mods_get_next_mod(&smods)) {
+ switch (mod->mod_op & ~LDAP_MOD_BVALUES ) {
+
+ case LDAP_MOD_DELETE:
+ if (mod->mod_bvalues != NULL ) {
+ break;
+ }
+
+ /*
+ * Here, check that we have the right to delete all
+ * the values of the attribute in the entry.
+ */
+
+ case LDAP_MOD_REPLACE:
+ if ( !lastmod ) {
+ if (be == NULL) {
+ if (slapi_pblock_get( pb, SLAPI_BACKEND, &be )) {
+ be = NULL;
+ }
+ }
+ if (be != NULL)
+ slapi_pblock_get ( pb, SLAPI_BE_LASTMOD, &lastmod );
+ }
+ if (lastmod &&
+ (strcmp (mod->mod_type, "modifiersname")== 0 ||
+ strcmp (mod->mod_type, "modifytimestamp")== 0)) {
+ continue;
+ }
+
+ slapi_entry_attr_find (e, mod->mod_type, &attr);
+ if ( attr != NULL) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal=NULL;
+ int k= slapi_attr_first_value(attr,&sval);
+ while(k != -1) {
+ attrVal = slapi_value_get_berval(sval);
+ rv = slapi_access_allowed (pb, e,
+ mod->mod_type,
+ (struct berval *)attrVal, /* XXXggood had to cast away const - BAD */
+ ACLPB_SLAPI_ACL_WRITE_DEL); /* was SLAPI_ACL_WRITE */
+ if ( rv != LDAP_SUCCESS) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return(rv);
+ }
+ k= slapi_attr_next_value(attr, k, &sval);
+ }
+ }
+ else {
+ rv = slapi_access_allowed (pb, e,
+ mod->mod_type,
+ NULL,
+ ACLPB_SLAPI_ACL_WRITE_DEL); /* was SLAPI_ACL_WRITE */
+ if ( rv != LDAP_SUCCESS) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return(rv);
+ }
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ /*
+ * Check that we have add/delete writes on the specific values
+ * we are trying to add.
+ */
+
+ if ( aclpb && aclpb->aclpb_curr_entry_sdn )
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+
+ if ( mod->mod_bvalues != NULL ) {
+
+ /*
+ * Here, there are specific values specified.
+ * For add and replace--we need add rights for these values.
+ * For delete we need delete rights for these values.
+ */
+
+ for ( i = 0; mod->mod_bvalues[i] != NULL; i++ ) {
+
+ if ( ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) ||
+ ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE)) {
+
+ rv = acl_access_allowed (pb,e,
+ mod->mod_type,
+ mod->mod_bvalues[i],
+ ACLPB_SLAPI_ACL_WRITE_ADD); /*was SLAPI_ACL_WRITE*/
+ } else if ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ rv = acl_access_allowed (pb,e,
+ mod->mod_type,
+ mod->mod_bvalues[i],
+ ACLPB_SLAPI_ACL_WRITE_DEL); /*was SLAPI_ACL_WRITE*/
+ } else {
+ rv = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ if ( rv != LDAP_SUCCESS ) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return rv;
+ }
+ /* Need to check for all the values because
+ ** we may be modifying a "self<right>" value.
+ */
+
+ /* Are we adding/replacing a aci attribute
+ ** value. In that case, we need to make
+ ** sure that the new value has thr right
+ ** syntax
+ */
+ if (strcmp(mod->mod_type,
+ aci_attr_type) == 0) {
+ if ( 0 != (rv = acl_verify_syntax( e_sdn,
+ mod->mod_bvalues[i]))) {
+ aclutil_print_err(rv, e_sdn,
+ mod->mod_bvalues[i],
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ } /* for */
+ }
+ } /* end of big for */
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return( LDAP_SUCCESS );
+}
+/***************************************************************************
+*
+* acl_modified
+* Modifies ( removed, add, changes) the ACI LIST.
+*
+* Input:
+* int *optype - op code
+* char *dn - DN of the entry
+* void *change - The change struct which contais the
+* - change value
+*
+* Returns:
+* None.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+extern void
+acl_modified (Slapi_PBlock *pb, int optype, char *n_dn, void *change)
+{
+ struct berval **bvalue;
+ char **value;
+ int rv=0; /* returned value */
+ char* new_RDN;
+ char* parent_DN;
+ char* new_DN;
+ LDAPMod **mods;
+ struct berval b;
+ int j;
+ Slapi_Attr *attr = NULL;
+ Slapi_Entry *e = NULL;
+ char ebuf [ BUFSIZ];
+ Slapi_DN *e_sdn;
+ aclUserGroup *ugroup = NULL;
+
+ e_sdn = slapi_sdn_new_ndn_byval ( n_dn );
+ /* Before we proceed, Let's first check if we are changing any groups.
+ ** If we are, then we need to change the signature
+ */
+ switch ( optype ) {
+ case SLAPI_OPERATION_MODIFY:
+ case SLAPI_OPERATION_DELETE:
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, (void*)&e);
+ break;
+ case SLAPI_OPERATION_ADD:
+ e = (Slapi_Entry *)change;
+ break;
+ }
+
+ /* e can be null for RDN */
+ if ( e ) slapi_entry_attr_find( e, "objectclass", &attr);
+
+ if ( attr ) {
+ int group_change = 0;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ i= slapi_attr_first_value ( attr,&sval );
+ while(i != -1) {
+ attrVal = slapi_value_get_berval ( sval );
+ if ( (strcasecmp (attrVal->bv_val, "groupOfNames") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfUniqueNames") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfCertificates") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfURLs") == 0 ) ) {
+ group_change= 1;
+ if ( optype == SLAPI_OPERATION_MODIFY ) {
+ Slapi_Attr *a = NULL;
+ int rv;
+ rv = slapi_entry_attr_find ( e, "uniqueMember", &a);
+ if ( rv != 0 ) break;
+ rv = slapi_entry_attr_find ( e, "Member", &a );
+ if ( rv != 0 ) break;
+ rv = slapi_entry_attr_find ( e, "MemberURL", &a );
+ if ( rv != 0 ) break;
+ /* That means we are not changing the member
+ ** list, so it's okay to let go this
+ ** change
+ */
+ group_change = 0;
+ }
+ break;
+ }
+ i= slapi_attr_next_value ( attr, i, &sval );
+ }
+
+ /*
+ ** We can do better here XXX, i.e invalidate the cache for users who
+ ** use this group. for now just do the whole thing.
+ */
+ if ( group_change ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Group Change: Invalidating entire UserGroup Cache\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION(n_dn, ebuf));
+ aclg_regen_group_signature();
+ if ( (optype == SLAPI_OPERATION_MODIFY) || (optype == SLAPI_OPERATION_DELETE ) ) {
+ /* Then we need to invalidate the acl signature also */
+ acl_signature = aclutil_gen_signature ( acl_signature );
+ }
+ }
+ }
+
+ /*
+ * Here if the target entry is in the group cache
+ * as a user then, as it's being changed it may move out of any dynamic
+ * groups it belongs to.
+ * Just remove it for now--can do better XXX by checking to see if
+ * it really needs to be removed by testing to see if he's
+ * still in th group after the change--but this requires keeping
+ * the memberURL of the group which we don't currently do.
+ * Also, if we keep
+ * the attributes that are being used in dynamic
+ * groups then we could only remove the user if a sensitive
+ * attribute was being modified (rather than scanning the whole user cache
+ * all the time). Also could do a hash lookup.
+ *
+ * aclg_find_userGroup() incs a refcnt so we can still refer to ugroup.
+ * aclg_markUgroupForRemoval() decs it and marks it for removal
+ * , so after that you cannot refer to ugroup.
+ *
+ */
+
+ if ( (ugroup = aclg_find_userGroup(n_dn)) != NULL) {
+ /*
+ * Mark this for deletion next time round--try to impact
+ * this mainline code as little as possible.
+ */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Marking entry %s for removal from ACL user Group Cache\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION(n_dn, ebuf));
+ aclg_markUgroupForRemoval (ugroup);
+ }
+
+ /*
+ * Take the write lock around all the mods--so that
+ * other operations will see the acicache either before the whole mod
+ * or after but not, as it was before, during the mod.
+ * This is in line with the LDAP concept of the operation
+ * on the whole entry being the atomic unit.
+ *
+ */
+
+ switch(optype) {
+ case SLAPI_OPERATION_DELETE:
+ /* In this case we have already checked if the user has
+ ** right to delete the entry. Part of delete of entry is
+ ** remove all the ACLs also.
+ */
+
+ acllist_acicache_WRITE_LOCK();
+ rv = acllist_remove_aci_needsLock(e_sdn, NULL);
+ acllist_acicache_WRITE_UNLOCK();
+
+ break;
+ case SLAPI_OPERATION_ADD:
+ slapi_entry_attr_find ( (Slapi_Entry *) change, aci_attr_type, &attr );
+
+ if ( attr ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ acllist_acicache_WRITE_LOCK();
+ i= slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval(sval);
+ rv= acllist_insert_aci_needsLock(e_sdn, attrVal );
+ if (rv <= ACL_ERR)
+ aclutil_print_err(rv, e_sdn, attrVal, NULL);
+ /* Print the aci list */
+ i= slapi_attr_next_value ( attr, i, &sval );
+ }
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ break;
+
+ case SLAPI_OPERATION_MODIFY:
+ {
+ int got_write_lock = 0;
+
+ mods = (LDAPMod **) change;
+
+ for (j=0; mods[j] != NULL; j++) {
+ if (strcasecmp(mods[j]->mod_type, aci_attr_type) == 0) {
+
+ /* Got an aci to mod in this list of mods, so
+ * take the acicache lock for the whole list of mods,
+ * remembering to free it below.
+ */
+ if ( !got_write_lock) {
+ acllist_acicache_WRITE_LOCK();
+ got_write_lock = 1;
+ }
+
+ switch (mods[j]->mod_op & ~LDAP_MOD_BVALUES) {
+ case LDAP_MOD_REPLACE:
+ /* First remove the item */
+ rv = acllist_remove_aci_needsLock(e_sdn, NULL);
+
+ /* now fall thru to add the new one */
+ case LDAP_MOD_ADD:
+ /* Add the new aci */
+ if (mods[j]->mod_op & LDAP_MOD_BVALUES) {
+ bvalue = mods[j]->mod_bvalues;
+ if (bvalue == NULL)
+ break;
+ for (; *bvalue != NULL; ++bvalue) {
+ rv=acllist_insert_aci_needsLock( e_sdn, *bvalue);
+ if (rv <= ACL_ERR) {
+ aclutil_print_err(rv, e_sdn,
+ *bvalue, NULL);
+ }
+ }
+ } else {
+ value = mods[j]->mod_values;
+ if (value == NULL)
+ break;
+ for (; *value != NULL; ++value) {
+ b.bv_len = strlen (*value);
+ b.bv_val = *value;
+ rv=acllist_insert_aci_needsLock( e_sdn, &b);
+ if (rv <= ACL_ERR) {
+ aclutil_print_err(rv, e_sdn,
+ &b, NULL);
+ }
+ }
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if (mods[j]->mod_op & LDAP_MOD_BVALUES) {
+ bvalue = mods[j]->mod_bvalues;
+ if (bvalue == NULL || *bvalue == NULL) {
+ rv = acllist_remove_aci_needsLock( e_sdn, NULL);
+ } else {
+ for (; *bvalue != NULL; ++bvalue)
+ acllist_remove_aci_needsLock( e_sdn, *bvalue);
+ }
+ } else {
+ value = mods[j]->mod_values;
+ if (value == NULL || *value == NULL) {
+ acllist_remove_aci_needsLock( e_sdn,NULL);
+ } else {
+ for (; *value != NULL; ++value) {
+ b.bv_len = strlen (*value);
+ b.bv_val = *value;
+ acllist_remove_aci_needsLock( e_sdn, &b);
+ }
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }/* modtype switch */
+ }/* attrtype is aci */
+ } /* end of for */
+ if ( got_write_lock ) {
+ acllist_acicache_WRITE_UNLOCK();
+ got_write_lock = 0;
+ }
+
+ break;
+ }/* case op is modify*/
+
+ case SLAPI_OPERATION_MODRDN:
+
+ new_RDN = (char*) change;
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl_modified (MODRDN %s => \"%s\"\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_dn, ebuf), new_RDN, 0);
+
+ /* compute new_DN: */
+ parent_DN = slapi_dn_parent (n_dn);
+ if (parent_DN == NULL) {
+ new_DN = new_RDN;
+ } else {
+ new_DN = (char*) slapi_ch_malloc (strlen (new_RDN) + 3
+ + strlen (parent_DN));
+ strcpy (new_DN, new_RDN);
+ strcat (new_DN, ",");
+ strcat (new_DN, parent_DN);
+ slapi_dn_normalize (new_DN);
+ }
+
+ /* Change the acls */
+ acllist_acicache_WRITE_LOCK();
+ acllist_moddn_aci_needsLock ( e_sdn, new_DN );
+ acllist_acicache_WRITE_UNLOCK();
+
+ /* deallocat the parent_DN */
+ if (parent_DN != NULL) {
+ slapi_ch_free ( (void **) &new_DN );
+ slapi_ch_free ( (void **) &parent_DN );
+ }
+ break;
+
+ default:
+ /* print ERROR */
+ break;
+ } /*optype switch */
+
+ slapi_sdn_free ( &e_sdn );
+
+}
+/***************************************************************************
+*
+* acl__scan_for_acis
+* Scan the list and picup the correct acls for evaluation.
+*
+* Input:
+* Acl_PBlock *aclpb - Main ACL pblock
+* int *err; - Any error status
+* Returns:
+* num_handles - Number of handles matched to the
+* - resource + 1.
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acl__scan_for_acis(Acl_PBlock *aclpb, int *err)
+{
+ aci_t *aci;
+ NSErr_t errp;
+ int attr_matched;
+ int deny_handle;
+ int allow_handle;
+ int gen_allow_handle = ACI_MAX_ELEVEL+1;
+ int gen_deny_handle = ACI_MAX_ELEVEL+1;
+ int i;
+ PRUint32 cookie;
+
+ TNF_PROBE_0_DEBUG(acl__scan_for_acis_start,"ACL","");
+
+ /*
+ ** Determine if we are traversing via the list Vs. we have our own
+ ** generated list
+ */
+ if ( aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST ||
+ aclpb->aclpb_handles_index[0] != -1 ) {
+ int kk = 0;
+ while ( kk < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[kk] != -1 ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Using ACL Cointainer:%d for evaluation\n", kk);
+ kk++;
+ }
+ }
+
+ memset (&errp, 0, sizeof(NSErr_t));
+ *err = ACL_FALSE;
+ aclpb->aclpb_num_deny_handles = -1;
+ aclpb->aclpb_num_allow_handles = -1;
+ for (i=0; i <= ACI_MAX_ELEVEL; i++) {
+ aclpb->aclpb_deny_handles [i] = NULL;
+ aclpb->aclpb_allow_handles [i] = NULL;
+ }
+
+ /* Check the signature. If it has changed, start fresh */
+ if ( aclpb->aclpb_signature != acl_signature ) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Restart the scan -- due to acl changes\n");
+ acllist_init_scan ( aclpb->aclpb_pblock, LDAP_SCOPE_BASE, NULL );
+ }
+
+ attr_matched = ACL_FALSE;
+ deny_handle = 0;
+ allow_handle = 0;
+ i = 0;
+
+ aclpb->aclpb_stat_acllist_scanned++;
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+
+ while( aci ) {
+ if (acl__resource_match_aci(aclpb, aci, 0, &attr_matched)) {
+ /* Generate the ACL list handle */
+ if (aci->aci_handle == NULL) {
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ continue;
+ }
+ aclutil_print_aci (aci, acl_access2str (aclpb->aclpb_access));
+
+ if (aci->aci_type & ACI_HAS_DENY_RULE) {
+ if (aclpb->aclpb_deny_handles[aci->aci_elevel] == NULL ) {
+ aclpb->aclpb_deny_handles[aci->aci_elevel] = aci;
+ } else {
+ if ((gen_deny_handle + ACI_DEFAULT_ELEVEL + 1) ==
+ aclpb->aclpb_deny_handles_size ) {
+ int num = ACLPB_INCR_LIST_HANDLES +
+ aclpb->aclpb_deny_handles_size;
+ /* allocate more space */
+ aclpb->aclpb_deny_handles =
+ (aci_t **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_deny_handles,
+ num * sizeof (aci_t *));
+ aclpb->aclpb_deny_handles_size = num;
+ }
+ aclpb->aclpb_deny_handles [gen_deny_handle] = aci;
+ gen_deny_handle++;
+ }
+ deny_handle++;
+ }
+ /*
+ ** It's possible that a single acl is in both the camps i.e
+ ** It has a allow and a deny rule
+ ** In that case we keep the same acl in both the camps.
+ */
+ if (aci->aci_type & ACI_HAS_ALLOW_RULE) {
+ if (aclpb->aclpb_allow_handles[aci->aci_elevel] == NULL ) {
+ aclpb->aclpb_allow_handles[aci->aci_elevel] = aci;
+ } else {
+ if ((gen_allow_handle + ACI_DEFAULT_ELEVEL + 1) ==
+ aclpb->aclpb_allow_handles_size) {
+ /* allocate more space */
+ int num = ACLPB_INCR_LIST_HANDLES +
+ aclpb->aclpb_allow_handles_size;
+
+ aclpb->aclpb_allow_handles =
+ (aci_t **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_allow_handles,
+ num * sizeof (aci_t *));
+ aclpb->aclpb_allow_handles_size = num;
+ }
+ aclpb->aclpb_allow_handles [gen_allow_handle] = aci;
+ gen_allow_handle++;
+ }
+ allow_handle++;
+ }
+ }
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ } /* end of while */
+
+ /* make the last one a null */
+ aclpb->aclpb_deny_handles [gen_deny_handle] = NULL;
+ aclpb->aclpb_allow_handles [gen_allow_handle] = NULL;
+
+ /* specify how many we found */
+ aclpb->aclpb_num_deny_handles = deny_handle;
+ aclpb->aclpb_num_allow_handles = allow_handle;
+
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Num of ALLOW Handles:%d, DENY handles:%d\n",
+ aclpb->aclpb_num_allow_handles, aclpb->aclpb_num_deny_handles, 0);
+
+ TNF_PROBE_0_DEBUG(acl__scan_for_acis_end,"ACL","");
+
+ return(allow_handle + deny_handle);
+}
+
+/***************************************************************************
+*
+* acl__resource_match_aci
+*
+* This compares the ACI for the given resource and determines if
+* the ACL applies to the resource or not.
+*
+* For read/search operation, we collect all the possible acls which
+* will apply, We will be using this list for future acl evaluation
+* for that entry.
+*
+* Input:
+* struct acl_pblock *aclpb - Main acl private block
+* aci_t *aci - The ACI item
+* int skip_attrEval - DOn't check for attrs
+* int *a_matched - Attribute matched
+*
+* Returns:
+*
+* ACL_TRUE - Yep, This ACL is applicable to the
+* - the resource.
+* ACL_FALSE - No it is not.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+#define ACL_RIGHTS_TARGETATTR_NOT_NEEDED ( SLAPI_ACL_ADD | SLAPI_ACL_DELETE | SLAPI_ACL_PROXY)
+static int
+acl__resource_match_aci( Acl_PBlock *aclpb, aci_t *aci, int skip_attrEval, int *a_matched)
+{
+
+ struct slapi_filter *f; /* filter */
+ int rv; /* return value */
+ int matches;
+ int attr_matched;
+ int attr_matched_in_targetattrfilters = 0;
+ int dn_matched;
+ char *res_attr;
+ int aci_right = 0;
+ int res_right = 0;
+ int star_matched = ACL_FALSE;
+ int num_attrs = 0;
+ AclAttrEval *c_attrEval = NULL;
+ const char *res_ndn = NULL;
+ const char *aci_ndn = NULL;
+ char *matched_val = NULL;
+ int add_matched_val_to_ht = 0;
+ char res_right_str[128];
+
+ TNF_PROBE_0_DEBUG(acl__resource_match_aci_start,"ACL","");
+
+ aclpb->aclpb_stat_aclres_matched++;
+
+ /* Assume that resource matches */
+ matches = ACL_TRUE;
+
+ /* Figure out if the acl has the correct rights or not */
+ aci_right = aci->aci_access;
+ res_right = aclpb->aclpb_access;
+ if (!(aci_right & res_right)) {
+ /* If we are looking for read/search and the acl has read/search
+ ** then go further because if targets match we may keep that
+ ** acl in the entry cache list.
+ */
+ if (!((res_right & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) &&
+ (aci_right & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ))))
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+
+ /* first Let's see if the entry is under the subtree where the
+ ** ACL resides. We can't let somebody affect a target beyond the
+ ** scope of where the ACL resides
+ ** Example: ACL is located in "ou=engineering, o=ace industry, c=us
+ ** but if the target is "o=ace industry, c=us", then we are in trouble.
+ **
+ ** If the aci is in the rootdse and the entry is not, then we do not
+ ** match--ie. acis in the rootdse do NOT apply below...for the moment.
+ **
+ */
+ res_ndn = slapi_sdn_get_ndn ( aclpb->aclpb_curr_entry_sdn );
+ aci_ndn = slapi_sdn_get_ndn ( aci->aci_sdn );
+ if (!slapi_sdn_issuffix(aclpb->aclpb_curr_entry_sdn, aci->aci_sdn)
+ || (!slapi_is_rootdse(res_ndn) && slapi_is_rootdse(aci_ndn)) ) {
+
+ /* cant' poke around */
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ ** We have a single ACI which we need to find if it applies to
+ ** the resource or not.
+ */
+ if ((aci->aci_type & ACI_TARGET_DN) &&
+ (aclpb->aclpb_curr_entry_sdn)) {
+ char *avaType;
+ struct berval *avaValue;
+
+ f = aci->target;
+ dn_matched = ACL_TRUE;
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ if (!slapi_dn_issuffix( res_ndn, avaValue->bv_val)) {
+ dn_matched = ACL_FALSE;
+ }
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+ }
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ if (aci->aci_type & ACI_TARGET_PATTERN) {
+
+ f = aci->target;
+ dn_matched = ACL_TRUE;
+
+ if ((rv = acl_match_substring(f, (char *)res_ndn, 0 /* match suffux */)) != ACL_TRUE) {
+ dn_matched = ACL_FALSE;
+ if(rv == ACL_ERR) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__resource_match_aci:pattern err\n",
+ 0,0,0);
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+ }
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+ }
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Is it a (target="ldap://cn=*,($dn),o=sun.com") kind of thing.
+ */
+ if (aci->aci_type & ACI_TARGET_MACRO_DN) {
+ /*
+ * See if the ($dn) component matches the string and
+ * retrieve the matched substring for later use
+ * in the userdn.
+ * The macro string is a function of the dn only, so if the
+ * entry is the same one don't recalculate it--
+ * this flag only works for search right now, could
+ * also optimise for mods by making it work for mods.
+ */
+
+ if ( (aclpb->aclpb_res_type & ACLPB_NEW_ENTRY) == 0 ) {
+ /*
+ * Here same entry so just look up the matched value,
+ * calculated from the targetdn and stored judiciously there
+ */
+ matched_val = (char *)acl_ht_lookup( aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index);
+ }
+ if ( matched_val == NULL &&
+ (aclpb->aclpb_res_type & (ACLPB_NEW_ENTRY | ACLPB_EFFECTIVE_RIGHTS))) {
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Evaluating macro aci(%d)%s for resource %s\n",
+ aci->aci_index, aci->aclName,
+ aclutil__access_str(res_right, res_right_str));
+ matched_val = acl_match_macro_in_target( res_ndn,
+ aci->aci_macro->match_this,
+ aci->aci_macro->macro_ptr);
+ add_matched_val_to_ht = 1; /* may need to add matched value to ht */
+ }
+ if (matched_val == NULL) {
+ dn_matched = ACL_FALSE;
+ } else {
+ dn_matched = ACL_TRUE;
+ }
+
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+
+ if ( add_matched_val_to_ht ) {
+ if ( matches == ACL_TRUE && matched_val ) {
+ /*
+ * matched_val may be needed later for matching on
+ * other targets or on the subject--so optimistically
+ * put it in the hash table.
+ * If, at the end of this routine, we
+ * find that after all the resource did not match then
+ * that's ok--the value is freed at aclpb cleanup time.
+ * If there is already an entry for this aci in this
+ * aclpb then remove it--it's an old value for a
+ * different entry.
+ */
+
+ acl_ht_add_and_freeOld(aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index,
+ matched_val);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "-- Added aci(%d) and matched value (%s) to macro ht\n",
+ aci->aci_index, matched_val);
+ acl_ht_display_ht(aclpb->aclpb_macro_ht);
+ } else {
+ slapi_ch_free((void **)&matched_val);
+ if (matches == ACL_FALSE) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Evaluated ACL_FALSE\n");
+ }
+ }
+ }
+ } /* MACRO_DN */
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ ** Here, if there's a targetfilter field, see if it matches.
+ **
+ ** The commented out code below was an erroneous attempt to skip
+ ** this test. It is wrong because: 1. you need to store
+ ** whether the last test matched or not (you cannot just assume it did)
+ ** and 2. It may not be the same aci, so the previous matched
+ ** value is a function of the aci.
+ ** May be interesting to build such a cache...but no evidence for
+ ** for that right now. See Bug 383424.
+ **
+ **
+ ** && ((aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST) ||
+ ** (aclpb->aclpb_res_type & ACLPB_NEW_ENTRY))
+ */
+ if (aci->aci_type & ACI_TARGET_FILTER ) {
+ int filter_matched = ACL_TRUE;
+
+ /*
+ * Check for macros.
+ * For targetfilter we need to fake the lasinfo structure--it's
+ * created "naturally" for subjects but not targets.
+ */
+
+
+ if ( aci->aci_type & ACI_TARGET_FILTER_MACRO_DN) {
+
+ lasInfo *lasinfo = NULL;
+
+ lasinfo = (lasInfo*) slapi_ch_malloc( sizeof(lasInfo) );
+
+ lasinfo->aclpb = aclpb;
+ lasinfo->resourceEntry = aclpb->aclpb_curr_entry;
+ aclpb->aclpb_curr_aci = aci;
+ filter_matched = aclutil_evaluate_macro( aci->targetFilterStr,
+ lasinfo,
+ ACL_EVAL_TARGET_FILTER);
+ slapi_ch_free((void**)&lasinfo);
+ } else {
+
+
+ if (slapi_vattr_filter_test(NULL, aclpb->aclpb_curr_entry,
+ aci->targetFilter,
+ 0 /*don't do acess chk*/)!= 0) {
+ filter_matched = ACL_FALSE;
+ }
+
+ }
+
+ /* If it's a logical value we can do logic on it...otherwise we do not match */
+ if ( filter_matched == ACL_TRUE || filter_matched == ACL_FALSE) {
+ if (aci->aci_type & ACI_TARGET_FILTER_NOT) {
+ matches = (filter_matched == ACL_TRUE ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (filter_matched == ACL_TRUE ? ACL_TRUE: ACL_FALSE);
+ }
+ } else {
+ matches = ACL_FALSE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for targetfilter evaluation.\n");
+ }
+
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+ }
+
+ /*
+ * Check to see if we need to evaluate any targetattrfilters.
+ * They look as follows:
+ * (targetattrfilters="add=sn:(sn=rob) && gn:(gn!=byrne),
+ * del=sn:(sn=rob) && gn:(gn=byrne)")
+ *
+ * For ADD/DELETE:
+ * If theres's a targetattrfilter then each add/del filter
+ * that applies to an attribute in the entry, must be satisfied
+ * by each value of the attribute in the entry.
+ *
+ * For MODIFY:
+ * If there's a targetattrfilter then the add/del filter
+ * must be satisfied by the attribute to be added/deleted.
+ * (MODIFY acl is evaluated one value at a time).
+ *
+ *
+ */
+
+ if ((aclpb->aclpb_access & SLAPI_ACL_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS )||
+ (aclpb->aclpb_access & SLAPI_ACL_DELETE &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) ) {
+
+ Targetattrfilter **attrFilterArray;
+
+ Targetattrfilter *attrFilter = NULL;
+
+ int found_applicable = 0;
+ Slapi_Attr *attr_ptr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrVal;
+ int k;
+ int done;
+
+
+ if (aclpb->aclpb_access & SLAPI_ACL_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ attrFilterArray = aci->targetAttrAddFilters;
+
+ } else if (aclpb->aclpb_access & SLAPI_ACL_DELETE &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS) {
+
+ attrFilterArray = aci->targetAttrDelFilters;
+
+ }
+
+ attr_matched = ACL_TRUE;
+ num_attrs = 0;
+
+ while (attrFilterArray[num_attrs] && attr_matched) {
+ attrFilter = attrFilterArray[num_attrs];
+
+ /*
+ * If this filter applies to an attribute in the entry,
+ * apply it to the entry.
+ * Otherwise just ignore it.
+ *
+ */
+
+ if (slapi_entry_attr_find ( aclpb->aclpb_curr_entry,
+ attrFilter->attr_str,
+ &attr_ptr) == 0) {
+
+ /*
+ * This is an applicable filter.
+ * The filter is to be appplied to the entry being added
+ * or deleted.
+ * The filter needs to be satisfied by _each_ occurence
+ * of the attribute in the entry--otherwise you
+ * could satisfy the filter and then put loads of other
+ * values in on the back of it.
+ */
+
+ found_applicable = 1;
+
+ sval=NULL;
+ attrVal=NULL;
+ k= slapi_attr_first_value(attr_ptr,&sval);
+ done = 0;
+ while(k != -1 && !done) {
+ attrVal = slapi_value_get_berval(sval);
+
+ if ( acl__make_filter_test_entry(
+ &aclpb->aclpb_filter_test_entry,
+ attrFilter->attr_str,
+ (struct berval *)attrVal) == LDAP_SUCCESS ) {
+
+ attr_matched= acl__test_filter(
+ aclpb->aclpb_filter_test_entry,
+ attrFilter->filter,
+ 1 /* Do filter sense evaluation below */
+ );
+ done = !attr_matched;
+ slapi_entry_free( aclpb->aclpb_filter_test_entry );
+ }
+
+ k= slapi_attr_next_value(attr_ptr, k, &sval);
+ }/* while */
+
+ /*
+ * Here, we applied an applicable filter to the entry.
+ * So if attr_matched is ACL_TRUE then every value
+ * of the attribute in the entry satisfied the filter.
+ * Otherwise, attr_matched is ACL_FALSE and not every
+ * value satisfied the filter, so we will teminate the
+ * scan of the filter list.
+ */
+
+ }
+
+ num_attrs++;
+ } /* while */
+
+ /*
+ * Here, we've applied all the applicable filters to the entry.
+ * Each one must have been satisfied by all the values of the attribute.
+ * The result of this is stored in attr_matched.
+ */
+
+#if 0
+ /*
+ * Don't support a notion of "add != " or "del != "
+ * at the moment.
+ * To do this, need to test whether it's an add test or del test
+ * then if it's add and ACI_TARGET_ATTR_ADD_FILTERS_NOT then
+ * flip the bit. Same for del.
+ */
+
+ if (aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS_NOT) {
+ matches = (matches ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (matches ? ACL_TRUE: ACL_FALSE);
+ }
+#endif
+
+ /* No need to look further */
+ if (attr_matched == ACL_FALSE) {
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ } else if ( (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) ||
+ (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_DEL &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) ) {
+
+
+ /*
+ * Here, it's a modify add/del and we have attr filters.
+ * So, we need to scan the add/del filter list to find the filter
+ * that applies to the current attribute.
+ * Then the (attribute,value) pair being added/deleted better
+ * match that filter.
+ *
+ *
+ */
+
+ Targetattrfilter **attrFilterArray = NULL;
+ Targetattrfilter *attrFilter;
+ int found = 0;
+
+ if (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ attrFilterArray = aci->targetAttrAddFilters;
+
+ } else if (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_DEL &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS) {
+
+ attrFilterArray = aci->targetAttrDelFilters;
+
+ }
+
+
+ /*
+ * Scan this filter list for an applicable filter.
+ */
+
+ found = 0;
+ num_attrs = 0;
+
+ while (attrFilterArray[num_attrs] && !found) {
+ attrFilter = attrFilterArray[num_attrs];
+
+ /* If this filter applies to the attribute, stop. */
+ if ((aclpb->aclpb_curr_attrEval) &&
+ slapi_attr_type_cmp ( aclpb->aclpb_curr_attrEval->attrEval_name,
+ attrFilter->attr_str, 1) == 0) {
+ found = 1;
+ }
+ num_attrs++;
+ }
+
+ /*
+ * Here, if found an applicable filter, then apply the filter to the
+ * (attr,val) pair.
+ * Otherwise, ignore the targetattrfilters.
+ */
+
+ if (found) {
+
+ if ( acl__make_filter_test_entry(
+ &aclpb->aclpb_filter_test_entry,
+ aclpb->aclpb_curr_attrEval->attrEval_name,
+ aclpb->aclpb_curr_attrVal) == LDAP_SUCCESS ) {
+
+ attr_matched= acl__test_filter(aclpb->aclpb_filter_test_entry,
+ attrFilter->filter,
+ 1 /* Do filter sense evaluation below */
+ );
+ slapi_entry_free( aclpb->aclpb_filter_test_entry );
+ }
+
+ /* No need to look further */
+ if (attr_matched == ACL_FALSE) {
+ matches = attr_matched;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Here this attribute appeared and was matched in a
+ * targetattrfilters list, so record this fact so we do
+ * not have to scan the targetattr list for the attribute.
+ */
+
+ attr_matched_in_targetattrfilters = 1;
+
+
+ }
+ } /* targetvaluefilters */
+
+
+ /* There are 3 cases by which acis are selected.
+ ** 1) By scanning the whole list and picking based on the resource.
+ ** 2) By picking a subset of the list which will be used for the whole
+ ** acl evaluation.
+ ** 3) A finer granularity, i.e, a selected list of acls which will be
+ ** used for only that entry's evaluation.
+ */
+ if ( !(skip_attrEval) && (aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_ENTRY_LIST) &&
+ (res_right & SLAPI_ACL_SEARCH) &&
+ ((aci->aci_access & SLAPI_ACL_READ) || (aci->aci_access & SLAPI_ACL_SEARCH))) {
+ int kk=0;
+
+ while ( kk < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[kk] >=0 ) kk++;
+ if (kk >= ACLPB_MAX_SELECTED_ACLS) {
+ aclpb->aclpb_state &= ~ACLPB_SEARCH_BASED_ON_ENTRY_LIST;
+ } else {
+ aclpb->aclpb_handles_index[kk++] = aci->aci_index;
+ aclpb->aclpb_handles_index[kk] = -1;
+ }
+ }
+
+
+ /* If we are suppose to skip attr eval, then let's skip it */
+ if ( (aclpb->aclpb_access & SLAPI_ACL_SEARCH ) && ( ! skip_attrEval ) &&
+ ( aclpb->aclpb_res_type & ACLPB_NEW_ENTRY )) {
+ aclEvalContext *c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+ int nhandle = c_evalContext->acle_numof_tmatched_handles;
+
+ if ( nhandle < ACLPB_MAX_SELECTED_ACLS) {
+ c_evalContext->acle_handles_matched_target[nhandle] = aci->aci_index;
+ c_evalContext->acle_numof_tmatched_handles++;
+ }
+ }
+
+ if ( skip_attrEval ) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /* We need to check again because we don't want to select this handle
+ ** if the right doesn't match for now.
+ */
+ if (!(aci_right & res_right)) {
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Here if the request is one that requires matching
+ * on a targetattr then do it here.
+ * If we have already matched an attribute in the targetattrfitlers list
+ * then we do not require a match in the targetattr so we can skip it.
+ * The operations that require targetattr are SLAPI_ACL_COMPARE,
+ * SLAPI_ACL_SEARCH, SLAPI_ACL_READ and SLAPI_ACL_WRITE, as long as
+ * c_attrEval is non-null (otherwise it's a modrdn op which
+ * does not require the targetattr list).
+ *
+ * rbyrneXXX if we had a proper permission for modrdn eg SLAPI_ACL_MODRDN
+ * then we would not need this crappy way of telling it was a MODRDN
+ * request ie. SLAPI_ACL_WRITE && !(c_attrEval).
+ */
+
+ c_attrEval = aclpb->aclpb_curr_attrEval;
+
+ /*
+ * If we've already matched on targattrfilter then do not
+ * bother to look at the attrlist.
+ */
+
+ if (!attr_matched_in_targetattrfilters) {
+
+ /* match target attr */
+ if ((c_attrEval) &&
+ (aci->aci_type & ACI_TARGET_ATTR)) {
+ /* there is a target ATTR */
+ Targetattr **attrArray = aci->targetAttr;
+ Targetattr *attr = NULL;
+
+ res_attr = c_attrEval->attrEval_name;
+ attr_matched = ACL_FALSE;
+ star_matched = ACL_FALSE;
+ num_attrs = 0;
+
+ while (attrArray[num_attrs] && !attr_matched) {
+ attr = attrArray[num_attrs];
+ if (attr->attr_type & ACL_ATTR_STRING) {
+ if (slapi_attr_type_cmp ( res_attr,
+ attr->u.attr_str, 1) == 0) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ }
+ } else if (attr->attr_type & ACL_ATTR_FILTER) {
+ if (ACL_TRUE == acl_match_substring (
+ attr->u.attr_filter,
+ res_attr, 1)) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ }
+ } else if (attr->attr_type & ACL_ATTR_STAR) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ star_matched = ACL_TRUE;
+ }
+ num_attrs++;
+ }
+
+ if (aci->aci_type & ACI_TARGET_ATTR_NOT) {
+ matches = (attr_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (attr_matched ? ACL_TRUE: ACL_FALSE);
+ }
+
+
+ aclpb->aclpb_state &= ~ACLPB_ATTR_STAR_MATCHED;
+ /* figure out how it matched, i.e star matched */
+ if (matches && star_matched && num_attrs == 1 &&
+ !(aclpb->aclpb_state & ACLPB_FOUND_ATTR_RULE))
+ aclpb->aclpb_state |= ACLPB_ATTR_STAR_MATCHED;
+ else {
+ /* we are here means that there is a specific
+ ** attr in the rule for this resource.
+ ** We need to avoid this case
+ ** Rule 1: (targetattr = "uid")
+ ** Rule 2: (targetattr = "*")
+ ** we cannot use STAR optimization
+ */
+ aclpb->aclpb_state |= ACLPB_FOUND_ATTR_RULE;
+ aclpb->aclpb_state &= ~ACLPB_ATTR_STAR_MATCHED;
+ }
+ } else if ( (c_attrEval) ||
+ (aci->aci_type & ACI_TARGET_ATTR)) {
+ if ((aci_right & ACL_RIGHTS_TARGETATTR_NOT_NEEDED) &&
+ (aclpb->aclpb_access & ACL_RIGHTS_TARGETATTR_NOT_NEEDED)) {
+ /*
+ ** Targetattr rule doesn't make any sense
+ ** in this case. So select this rule
+ ** default: matches = ACL_TRUE;
+ */
+ ;
+ } else if (aci_right & SLAPI_ACL_WRITE &&
+ (aci->aci_type & ACI_TARGET_ATTR) &&
+ !(c_attrEval)) {
+ /* We need to handle modrdn operation. Modrdn doesn't
+ ** change any attrs but changes the RDN and so (attr=NULL).
+ ** Here we found an acl which has a targetattr but
+ ** the resource doesn't need one. In that case, we should
+ ** consider this acl.
+ ** default: matches = ACL_TRUE;
+ */
+ ;
+ } else {
+ matches = ACL_FALSE;
+ }
+ }
+ }/* !attr_matched_in_targetattrfilters */
+
+ /*
+ ** Here we are testing if we find a entry test rule (which should
+ ** be rare). In that case, just remember it. An entry test rule
+ ** doesn't have "(targetattr)".
+ */
+ if (aclpb && (aclpb->aclpb_state & ACLPB_EVALUATING_FIRST_ATTR) &&
+ (!(aci->aci_type & ACI_TARGET_ATTR))) {
+ aclpb->aclpb_state |= ACLPB_FOUND_A_ENTRY_TEST_RULE;
+ }
+
+ /*
+ * Generic exit point for this routine:
+ * matches is ACL_TRUE if the aci matches the target of the resource,
+ * ACL_FALSE othrewise.
+ * Apologies for the goto--this is a retro-fitted exit point.
+ */
+acl__resource_match_aci_EXIT:
+
+ /*
+ * For macro acis, there may be a partial macro string
+ * placed in the aclpb_macro_ht
+ * even if the aci did not finally match.
+ * All the partial strings will be freed at aclpb
+ * cleanup time.
+ */
+
+ TNF_PROBE_0_DEBUG(acl__resource_match_aci_end,"ACL","");
+
+ return (matches);
+}
+/* Macro to determine if the cached result is valid or not. */
+#define ACL_CACHED_RESULT_VALID( result) \
+ (((result & ACLPB_CACHE_READ_RES_ALLOW) && \
+ (result & ACLPB_CACHE_READ_RES_SKIP)) || \
+ ((result & ACLPB_CACHE_SEARCH_RES_ALLOW) && \
+ (result & ACLPB_CACHE_SEARCH_RES_SKIP))) ? 0 : 1
+/***************************************************************************
+*
+* acl__TestRights
+*
+* Test the rights and find out if access is allowed or not.
+*
+* Processing Alogorithm:
+*
+* First, process the DENY rules one by one. If the user is not explicitly
+* denied, then check if the user is allowed by processing the ALLOW handles
+* one by one.
+* The result of the evaluation is cached. Exceptions are
+* -- If an acl happens to be in both DENY and ALLOW camp.
+* -- Only interested for READ/SEARCH right.
+*
+* Input:
+* struct acl_pblock *aclpb - main acl private block
+* int access - The access bits
+* char **right - The right we are looking for
+* char ** generic - Generic rights
+*
+* Returns:
+*
+* ACL_RES_ALLOW - Access allowed
+* ACL_RES_DENY - Access denied
+* err - error condition
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acl__TestRights(Acl_PBlock *aclpb,int access, char **right, char ** map_generic,
+ aclResultReason_t *result_reason)
+{
+ ACLEvalHandle_t *acleval;
+ int rights_rv = ACL_RES_DENY;
+ int rv, i,j, k;
+ int index;
+ char *deny = NULL;
+ char *deny_generic = NULL;
+ char *acl_tag;
+ int expr_num;
+ char *testRights[2];
+ aci_t *aci;
+ int numHandles = 0;
+ aclEvalContext *c_evalContext = NULL;
+
+ TNF_PROBE_0_DEBUG(acl__TestRights_start,"ACL","");
+
+ c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+
+ /* record the aci and reason for access decision */
+ result_reason->deciding_aci = NULL;
+ result_reason->reason = ACL_REASON_NONE;
+
+ /* If we don't have any ALLLOW handles, it's DENY by default */
+ if (aclpb->aclpb_num_allow_handles <= 0) {
+ result_reason->deciding_aci = NULL;
+ result_reason->reason = ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS;
+
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,no_allows,"");
+
+ return ACL_RES_DENY;
+ }
+
+ /* Get the ACL evaluation Context */
+ acleval = aclpb->aclpb_acleval;
+
+ testRights[0] = *right;
+ testRights[1] = '\0';
+
+ /*
+ ** START PROCESSING DENY HANDLES
+ ** Process each handle at a time. Do not concatenate the handles or else
+ ** all the context information will be build again and we will pay a
+ ** lot of penalty. The context is built the first time the handle is
+ ** processed.
+ **
+ ** First we set the default to INVALID so that if rules are not matched, then
+ ** we get INVALID and if a rule matched, the we get DENY.
+ */
+ aclpb->aclpb_state &= ~ACLPB_EXECUTING_ALLOW_HANDLES;
+ aclpb->aclpb_state |= ACLPB_EXECUTING_DENY_HANDLES;
+ ACL_SetDefaultResult (NULL, acleval, ACL_RES_INVALID);
+
+ numHandles = ACI_MAX_ELEVEL + aclpb->aclpb_num_deny_handles;
+ for (i=0, k=0; i < numHandles && k < aclpb->aclpb_num_deny_handles ; ++i) {
+ int skip_eval = 0;
+
+ /*
+ ** If the handle has been evaluated before, we can
+ ** cache the result.
+ */
+ if (((aci = aclpb->aclpb_deny_handles[i]) == NULL) && (i <= ACI_MAX_ELEVEL))
+ continue;
+ k++;
+ index = aci->aci_index;
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Evaluating DENY aci(%d) \"%s\"\n", index, aci->aclName);
+
+ if (access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ /*
+ * aclpb->aclpb_cache_result[0..aclpb->aclpb_last_cache_result] is
+ * a cache of info about whether applicable acis
+ * allowed, did_not_allow or denied access
+ */
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ if ( result & ACLPB_CACHE_SEARCH_RES_DENY){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found SEARCH DENY in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_deny,"");
+ return ACL_RES_DENY;
+ } else if ((result & ACLPB_CACHE_SEARCH_RES_SKIP) ||
+ (result & ACLPB_CACHE_SEARCH_RES_ALLOW)) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found SEARCH SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ } else { /* must be READ */
+ if (result & ACLPB_CACHE_READ_RES_DENY) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found READ DENY in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_deny,"");
+ return ACL_RES_DENY;
+ } else if ( result & ACLPB_CACHE_READ_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found READ SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (skip_eval) {
+ skip_eval = 0;
+ continue;
+ }
+
+ rv = ACL_EvalSetACL(NULL, acleval, aci->aci_handle);
+ if ( rv < 0) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__TestRights:Unable to set the DENY acllist\n",
+ 0,0,0);
+ continue;
+ }
+ /*
+ ** Now we have all the information we need. We need to call
+ ** the ONE ACL to test the rights.
+ ** return value: ACL_RES_DENY, ACL_RES_ALLOW, error codes
+ */
+ aclpb->aclpb_curr_aci = aci;
+ rights_rv = ACL_EvalTestRights (NULL, acleval, testRights,
+ map_generic, &deny,
+ &deny_generic,
+ &acl_tag, &expr_num);
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Processed:%d DENY handles Result:%d\n",index, rights_rv,0);
+
+ if (rights_rv == ACL_RES_FAIL) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_NONE;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ }
+
+ /* have we executed an ATTR RULE */
+ if ( aci->aci_ruleType & ACI_ATTR_RULES )
+ aclpb->aclpb_state |= ACLPB_ATTR_RULE_EVALUATED;
+
+ if (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS ) {
+ /* j == aclpb->aclpb_last_cache_result &&
+ j < ACLPB_MAX_CACHE_RESULTS */
+ aclpb->aclpb_last_cache_result++;
+ aclpb->aclpb_cache_result[j].aci_index = index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+
+ } else { /* cache overflow */
+ if ( rights_rv == ACL_RES_DENY) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ } else {
+ continue;
+ }
+ }
+
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ if (rights_rv == ACL_RES_DENY) {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_DENY;
+ } else { /* MUST BE READ */
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_DENY;
+ }
+ /* We are done -- return */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ } else if (rights_rv == ACL_RES_ALLOW) {
+ /* This will happen, of we have an acl with both deny and allow
+ ** Since we may not have finished all the deny acl, go thru all
+ ** of them. We will use this cached result when we evaluate this
+ ** handle in the context of allow handles.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_ALLOW;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_ALLOW;
+ }
+
+ } else {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_SKIP;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_SKIP;
+ }
+ continue;
+ }
+ } else {
+ if ( rights_rv == ACL_RES_DENY ) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ }
+ }
+ }
+
+
+ /*
+ ** START PROCESSING ALLOW HANDLES.
+ ** Process each handle at a time. Do not concatenate the handles or else
+ ** all the context information will be build again and we will pay a
+ ** lot of penalty. The context is built the first time the handle is
+ ** processed.
+ **
+ ** First we set the default to INVALID so that if rules are not matched, then
+ ** we get INVALID and if a rule matched, the we get ALLOW.
+ */
+ aclpb->aclpb_state &= ~ACLPB_EXECUTING_DENY_HANDLES;
+ aclpb->aclpb_state |= ACLPB_EXECUTING_ALLOW_HANDLES;
+ ACL_SetDefaultResult (NULL, acleval, ACL_RES_INVALID);
+ numHandles = ACI_MAX_ELEVEL + aclpb->aclpb_num_allow_handles;
+ for (i=0, k=0; i < numHandles && k < aclpb->aclpb_num_allow_handles ; ++i) {
+ int skip_eval = 0;
+ /*
+ ** If the handle has been evaluated before, we can
+ ** cache the result.
+ */
+ aci = aclpb->aclpb_allow_handles[i];
+ if (((aci = aclpb->aclpb_allow_handles[i]) == NULL) && (i <= ACI_MAX_ELEVEL))
+ continue;
+ k++;
+ index = aci->aci_index;
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "%d. Evaluating ALLOW aci(%d) \"%s\"\n", k, index, aci->aclName);
+
+ if (access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ /*
+ * aclpb->aclpb_cache_result[0..aclpb->aclpb_last_cache_result] is
+ * a cache of info about whether applicable acis
+ * allowed, did_not_allow or denied access
+ */
+
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ if (result & ACLPB_CACHE_SEARCH_RES_ALLOW) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found SEARCH ALLOW in cache\n");
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_allow,"");
+ return ACL_RES_ALLOW;
+ } else if ( result & ACLPB_CACHE_SEARCH_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found SEARCH SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ /* need to evaluate */
+ break;
+ }
+ } else {
+ if ( result & ACLPB_CACHE_READ_RES_ALLOW) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found READ ALLOW in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_allow,"");
+ return ACL_RES_ALLOW;
+ } else if ( result & ACLPB_CACHE_READ_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found READ SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if ( skip_eval) {
+ skip_eval = 0;
+ continue;
+ }
+
+ TNF_PROBE_0_DEBUG(acl__libaccess_start,"ACL","");
+ rv = ACL_EvalSetACL(NULL, acleval, aci->aci_handle);
+ if ( rv < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl__TestRights:Unable to set the acllist\n",
+ 0,0,0);
+ continue;
+ }
+ /*
+ ** Now we have all the information we need. We need to call
+ ** the ONE ACL to test the rights.
+ ** return value: ACL_RES_DENY, ACL_RES_ALLOW, error codes
+ */
+ aclpb->aclpb_curr_aci = aci;
+ rights_rv = ACL_EvalTestRights (NULL, acleval, testRights,
+ map_generic, &deny,
+ &deny_generic,
+ &acl_tag, &expr_num);
+ TNF_PROBE_0_DEBUG(acl__libaccess_end,"ACL","");
+
+ if (aci->aci_ruleType & ACI_ATTR_RULES)
+ aclpb->aclpb_state |= ACLPB_ATTR_RULE_EVALUATED;
+
+ if (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS ) {
+ /* j == aclpb->aclpb_last_cache_result &&
+ j < ACLPB_MAX_CACHE_RESULTS */
+ aclpb->aclpb_last_cache_result++;
+ aclpb->aclpb_cache_result[j].aci_index = index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+ } else { /* cache overflow */
+ slapi_log_error (SLAPI_LOG_FATAL, "acl__TestRights", "cache overflown\n");
+ if ( rights_rv == ACL_RES_ALLOW) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+ } else {
+ continue;
+ }
+ }
+
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ if (rights_rv == ACL_RES_ALLOW) {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_ALLOW;
+ } else { /* must be READ */
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_ALLOW;
+ }
+
+ /* We are done -- return */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+
+ } else {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_SKIP;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_SKIP;
+ }
+ continue;
+ }
+ } else {
+ if ( rights_rv == ACL_RES_ALLOW ) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+ }
+ }
+ }/* for */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS;
+
+ TNF_PROBE_0_DEBUG(acl__TestRights_end,"ACL","");
+
+ return (ACL_RES_DENY);
+}
+/***************************************************************************
+*
+* acl_match_substring
+*
+* Compare the input string to the patteren in the filter
+*
+* Input:
+* struct slapi_filter *f - Filter which has the patteren
+* char *str - String to compare
+* int exact_match - 1; match the pattern exactly
+* - 0; match the pattern as a suffix
+*
+* Returns:
+* ACL_TRUE - The sting matches with the patteren
+* ACL_FALSE - No it doesn't
+* ACL_ERR - Error
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_match_substring ( Slapi_Filter *f, char *str, int exact_match)
+{
+ int i, rc, len;
+ char *p;
+ char *end, *realval, *tmp;
+ char pat[BUFSIZ];
+ char buf[BUFSIZ];
+ char *type, *initial, *final;
+ char **any;
+
+ if ( 0 != slapi_filter_get_subfilt ( f, &type, &initial, &any, &final ) ) {
+ return (ACL_FALSE);
+ }
+
+ /* convert the input to lower. */
+ for (p = str; *p; p++)
+ *p = TOLOWER ( *p );
+
+ /* construct a regular expression corresponding to the filter: */
+ pat[0] = '\0';
+ p = pat;
+ end = pat + sizeof(pat) - 2; /* leave room for null */
+
+
+ if ( initial != NULL) {
+ strcpy (p, "^");
+ p = strchr (p, '\0');
+
+ /* 2 * in case every char is special */
+ if (p + 2 * strlen ( initial ) > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+
+ return (ACL_ERR);
+ }
+
+ if (!exact_match) {
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ }
+ acl_strcpy_special (p, initial);
+ p = strchr (p, '\0');
+ }
+
+ if ( any != NULL) {
+ for (i = 0; any && any[i] != NULL; i++) {
+ /* ".*" + value */
+ if (p + 2 * strlen ( any[i]) + 2 > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+ return (ACL_ERR);
+ }
+
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ acl_strcpy_special (p, any[i]);
+ p = strchr (p, '\0');
+ }
+ }
+
+
+ if ( final != NULL) {
+ /* ".*" + value */
+ if (p + 2 * strlen ( final ) + 2 > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+ return (ACL_ERR);
+ }
+
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ acl_strcpy_special (p, final);
+ p = strchr (p, '\0');
+ strcpy (p, "$");
+ }
+
+ /* see if regex matches with the input string */
+ tmp = NULL;
+ len = strlen(str);
+
+ if (len < sizeof(buf)) {
+ strcpy (buf, str);
+ realval = buf;
+ } else {
+ tmp = (char*) slapi_ch_malloc (len + 1);
+ strcpy (tmp, str);
+ realval = tmp;
+ }
+
+ slapi_dn_normalize (realval);
+
+
+ /* What we have built is a regular pattaren expression.
+ ** Now we will compile the pattern and compare wth the string to
+ ** see if the input string matches with the patteren or not.
+ */
+ slapd_re_lock();
+ if ((p = slapd_re_comp (pat)) != 0) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl_match_substring:re_comp failed (%s)\n", p, 0, 0);
+ slapd_re_unlock();
+ return (ACL_ERR);
+ }
+
+ /* re_exec() returns 1 if the string p1 matches the last compiled
+ ** regular expression, 0 if the string p1 failed to match
+ ** (see man pages)
+ **
+ ** IMPORTANT NOTE: If I use compile() and step() to do the patteren
+ ** matching, it seems that step() is leaking 1036 bytes/search
+ ** I couldn't figure out why it's leaking.
+ */
+ rc = slapd_re_exec( realval );
+
+ slapd_re_unlock();
+
+ if (tmp != NULL) {
+ slapi_ch_free ( (void **) &tmp );
+ }
+
+ if (rc == 1) {
+ return ACL_TRUE;
+ } else {
+ return ACL_FALSE;
+ }
+}
+/***************************************************************************
+*
+* acl__reset_cached_result
+*
+* Go thru the cached result and invlalidate the cached evaluation results for
+* rules which can only be cached based on the scope.
+* If we have scope ACI_CACHE_RESULT_PER_ENTRY, then we need to invalidate the
+* cacched reult whenever we hit a new entry.
+*
+* Returns:
+* Null
+*
+**************************************************************************/
+static void
+acl__reset_cached_result (struct acl_pblock *aclpb )
+{
+
+ int j;
+
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ /* Unless we have to clear the result, move on */
+ if (!( aclpb->aclpb_cache_result[j].aci_ruleType & ACI_CACHE_RESULT_PER_ENTRY))
+ continue;
+ aclpb->aclpb_cache_result[j].result = 0;
+ }
+}
+/*
+ * acl_access_allowed_disjoint_resource
+ *
+ * This is an internal module which can be used to verify
+ * access to a resource which may not be inside the search scope.
+ *
+ * Returns:
+ * - Same return val as acl_access_allowed().
+ */
+int
+acl_access_allowed_disjoint_resource(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* access rights */
+ )
+{
+
+ int rv;
+ struct acl_pblock *aclpb = NULL;
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ /*
+ ** It's possible that we already have a context of ACLs.
+ ** However once in a while, we need to test
+ ** access to a resource which (like vlv, schema) which falls
+ ** outside the search base. In that case, we need to go
+ ** thru all the ACLs and not depend upon the acls which we have
+ ** gathered.
+ */
+
+ /* If you have the right to use the resource, then we don't need to check for
+ ** proxy right on that resource.
+ */
+ if (aclpb)
+ aclpb->aclpb_state |= ( ACLPB_DONOT_USE_CONTEXT_ACLS| ACLPB_DONOT_EVALUATE_PROXY );
+
+ rv = acl_access_allowed(pb, e, attr, val, access);
+
+ if (aclpb) aclpb->aclpb_state &= ~ACLPB_DONOT_USE_CONTEXT_ACLS;
+ if (aclpb ) aclpb->aclpb_state &= ~ACLPB_DONOT_EVALUATE_PROXY;
+
+ return rv;
+}
+
+/*
+ * acl__attr_cached_result
+ * Loops thru the cached result and determines if we can use the cached value.
+ *
+ * Inputs:
+ * Slapi_pblock *aclpb - acl private block
+ * char *attr - attribute name
+ * int access - access type
+ * Returns:
+ * LDAP_SUCCESS: - access is granted
+ * LDAP_INSUFFICIENT_ACCESS - access denied
+ * ACL_ERR - no cached info about this attr.
+ * - or the attr had multiple result and so
+ * - we can't determine the outcome.
+ *
+ */
+static int
+acl__attr_cached_result (struct acl_pblock *aclpb, char *attr, int access )
+{
+
+ int i, rc;
+ aclEvalContext *c_evalContext;
+
+ if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ) ))
+ return ACL_ERR;
+
+ if (aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acl__attr_cached_result:Using Context: ACLPB_ACLCB\n", 0,0,0 );
+ } else {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acl__attr_cached_result:Using Context: ACLPB_PREV\n", 0,0,0 );
+ }
+
+ if ( attr == NULL ) {
+ int eval_read = 0;
+ /*
+ ** Do I have access to at least one attribute, then I have
+ ** access to the entry.
+ */
+ for (i=0; i < c_evalContext->acle_numof_attrs; i++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[i];
+
+ if ( (access & SLAPI_ACL_READ ) && a_eval->attrEval_r_status &&
+ a_eval->attrEval_r_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ eval_read++;
+ if ( a_eval->attrEval_r_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ /* rbyrne: recompute if we have to.
+ * How does this cached result get turned off for
+ * attr style acis which acannot be cached becuase entry
+ * can result in a diff value.
+ */
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_r_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ if ( rc == LDAP_SUCCESS) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+ }/* for */
+ /*
+ * If we have scanned the whole list without success then
+ * we are not granting access to this entry through access
+ * to an attribute in the list--however this does not mean
+ * that we do not have access to the entry via another attribute
+ * not already in the list, so return -1 meaning
+ * "don't know".
+ */
+ return(ACL_ERR);
+#if 0
+ if ( eval_read )
+ return LDAP_INSUFFICIENT_ACCESS;
+ else
+ return ACL_ERR;
+#endif
+ }
+
+ for (i=0; i < c_evalContext->acle_numof_attrs; i++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[i];
+
+ if ( a_eval == NULL ) continue;
+
+ if (strcasecmp ( attr, a_eval->attrEval_name ) == 0 ) {
+ if ( access & SLAPI_ACL_SEARCH ) {
+ if (a_eval->attrEval_s_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ if ( a_eval->attrEval_s_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ else if ( a_eval->attrEval_s_status & ACL_ATTREVAL_FAIL)
+ return LDAP_INSUFFICIENT_ACCESS;
+ else if ( a_eval->attrEval_s_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_s_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ } else
+ return ACL_ERR;
+ } else {
+ /* This means that for the same attribute and same type of
+ ** access, we had different results at different time.
+ ** Since we are not caching per object, we can't
+ ** determine exactly. So, can't touch this
+ */
+ return ACL_ERR;
+ }
+ } else {
+ if (a_eval->attrEval_r_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ if ( a_eval->attrEval_r_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_FAIL)
+ return LDAP_INSUFFICIENT_ACCESS;
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_r_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ } else
+ return ACL_ERR;
+ } else {
+ /* Look above for explanation */
+ return ACL_ERR;
+ }
+ }
+ }
+ }
+ return ACL_ERR;
+}
+
+/*
+ * Had to do this juggling of casting to make
+ * both Nt & unix compiler happy.
+ */
+static int
+acl__cmp(const void *a, const void *b)
+{
+ short *i = (short *) a;
+ short *j = (short *) b;
+
+ if ( (short) *i > (short) *j )
+ return (1);
+ if ( (short)*i < (short) *j)
+ return (-1);
+ return (0);
+}
+
+/*
+ * acl__scan_match_handles
+ * Go thru the ACL list and determine if the list of acls selected matches
+ * what we have in the cache.
+ *
+ * Inputs:
+ * Acl_PBlock *pb - Main pblock ( blacvk hole)
+ * int type - Which context to look on
+ *
+ * Returns:
+ * 0 - matches all the acl handles
+ * ACL_ERR - sorry; no match
+ *
+ * ASSUMPTION: A READER LOCK ON ACL LIST
+ */
+static int
+acl__scan_match_handles ( Acl_PBlock *aclpb, int type)
+{
+
+
+ int matched = 0;
+ aci_t *aci = NULL;
+ int index;
+ PRUint32 cookie;
+ aclEvalContext *c_evalContext = NULL;
+
+ if (type == ACLPB_EVALCONTEXT_PREV ) {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ } else if ( type == ACLPB_EVALCONTEXT_ACLCB ){
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ } else {
+ return ACL_ERR;
+ }
+
+
+ if ( !c_evalContext->acle_numof_tmatched_handles )
+ return ACL_ERR;
+
+ aclpb->aclpb_stat_acllist_scanned++;
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+
+ while ( aci ) {
+ index = aci->aci_index;
+ if (acl__resource_match_aci(aclpb, aci, 1 /* skip attr matching */, NULL )) {
+ int j;
+ int s_matched = matched;
+
+ /* We have a sorted list of handles that matched the target */
+
+ for (j=0; j < c_evalContext->acle_numof_tmatched_handles ; j++ ) {
+ if ( c_evalContext->acle_handles_matched_target[j] > index )
+
+ break;
+ else if ( index == c_evalContext->acle_handles_matched_target[j] ) {
+ int jj;
+ matched++;
+
+ /* See if this is a ATTR rule that matched -- in that case we have
+ ** to nullify the cached result
+ */
+ if ( aci->aci_ruleType & ACI_ATTR_RULES ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Found an attr Rule [Name:%s Index:%d\n", aci->aclName,
+ aci->aci_index );
+ for ( jj =0; jj < c_evalContext->acle_numof_attrs; jj++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[jj];
+ if ( a_eval->attrEval_r_aciIndex == aci->aci_index )
+ a_eval->attrEval_r_status = ACL_ATTREVAL_RECOMPUTE;
+ if ( a_eval->attrEval_s_aciIndex == aci->aci_index )
+ a_eval->attrEval_s_status = ACL_ATTREVAL_RECOMPUTE;
+ }
+ }
+ break;
+ }
+ }
+ if ( s_matched == matched ) return ACL_ERR;
+ }
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ }
+ if ( matched == c_evalContext->acle_numof_tmatched_handles )
+ return 0;
+
+ return ACL_ERR;
+}
+/*
+ * acl_copyEval_context
+ * Copy the context info which include attr info and handles.
+ *
+ * Inputs
+ * struct acl_pblock *aclpb - acl private main block
+ * aclEvalContext *src - src context
+ * aclEvalContext *dest - dest context
+ * Returns:
+ * None.
+ *
+ */
+void
+acl_copyEval_context ( struct acl_pblock *aclpb, aclEvalContext *src,
+ aclEvalContext *dest , int copy_attr_only )
+{
+
+ int d_slot, i;
+
+ /* Do a CLEAN copy we have nothing or else do an incremental copy.*/
+ if ( src->acle_numof_attrs < 1 )
+ return;
+
+ /* Copy the attr info */
+ if ( dest->acle_numof_attrs < 1 )
+ acl_clean_aclEval_context ( dest, 0 /*clean */ );
+
+ d_slot = dest->acle_numof_attrs;
+ for (i=0; i < src->acle_numof_attrs; i++ ) {
+ int j;
+ int attr_exists = 0;
+ int dd_slot = d_slot;
+
+ if ( aclpb && (i == 0) ) aclpb->aclpb_stat_num_copycontext++;
+
+ if ( src->acle_attrEval[i].attrEval_r_status == 0 &&
+ src->acle_attrEval[i].attrEval_s_status == 0 )
+ continue;
+
+ for ( j = 0; j < dest->acle_numof_attrs; j++ ) {
+ if ( strcasecmp ( src->acle_attrEval[i].attrEval_name,
+ dest->acle_attrEval[j].attrEval_name ) == 0 ) {
+ /* We have it. skip it. */
+ attr_exists = 1;
+ dd_slot = j;
+ break;
+ }
+ }
+ if ( !attr_exists ) {
+ if ( dd_slot >= ACLPB_MAX_ATTRS -1 )
+ break;
+
+ if ( aclpb) aclpb->aclpb_stat_num_copy_attrs++;
+
+ if ( dest->acle_attrEval[dd_slot].attrEval_name )
+ slapi_ch_free ( (void **) &dest->acle_attrEval[dd_slot].attrEval_name );
+
+ dest->acle_attrEval[dd_slot].attrEval_name =
+ slapi_ch_strdup ( src->acle_attrEval[i].attrEval_name );
+ }
+ /* Copy the result status and the aci index */
+ dest->acle_attrEval[dd_slot].attrEval_r_status =
+ src->acle_attrEval[i].attrEval_r_status;
+ dest->acle_attrEval[dd_slot].attrEval_r_aciIndex =
+ src->acle_attrEval[i].attrEval_r_aciIndex;
+ dest->acle_attrEval[dd_slot].attrEval_s_status =
+ src->acle_attrEval[i].attrEval_s_status;
+ dest->acle_attrEval[dd_slot].attrEval_s_aciIndex =
+ src->acle_attrEval[i].attrEval_s_aciIndex;
+
+ if (!attr_exists ) d_slot++;
+ }
+
+ dest->acle_numof_attrs = d_slot;
+ dest->acle_attrEval[d_slot].attrEval_name = NULL;
+
+ if ( copy_attr_only )
+ return;
+
+ /* First sort the arrays which keeps the acl index numbers */
+ qsort ( (char *) src->acle_handles_matched_target,
+ (size_t)src->acle_numof_tmatched_handles, sizeof( int ), acl__cmp );
+
+ for (i=0; i < src->acle_numof_tmatched_handles; i++ ) {
+ dest->acle_handles_matched_target[i] =
+ src->acle_handles_matched_target[i];
+ }
+
+ if ( src->acle_numof_tmatched_handles ) {
+ dest->acle_numof_tmatched_handles = src->acle_numof_tmatched_handles;
+ if ( aclpb) aclpb->aclpb_stat_num_tmatched_acls = src->acle_numof_tmatched_handles;
+ }
+}
+
+/*
+ * acl_clean_aclEval_context
+ * Clean the eval context
+ *
+ * Inputs:
+ * aclEvalContext *clean_me - COntext to be cleaned
+ * int clean_type - 0: clean, 1 scrub
+ *
+ */
+
+void
+acl_clean_aclEval_context ( aclEvalContext *clean_me, int scrub_only )
+{
+ int i;
+
+ /* Copy the attr info */
+ for (i=0; i < clean_me->acle_numof_attrs; i++ ) {
+
+ char *a_name = clean_me->acle_attrEval[i].attrEval_name;
+ if ( a_name && !scrub_only) {
+ slapi_ch_free ( (void **) &a_name );
+ clean_me->acle_attrEval[i].attrEval_name = NULL;
+ }
+ clean_me->acle_attrEval[i].attrEval_r_status = 0;
+ clean_me->acle_attrEval[i].attrEval_s_status = 0;
+ clean_me->acle_attrEval[i].attrEval_r_aciIndex = 0;
+ clean_me->acle_attrEval[i].attrEval_s_aciIndex = 0;
+ }
+
+ if ( !scrub_only ) clean_me->acle_numof_attrs = 0;
+ clean_me->acle_numof_tmatched_handles = 0;
+}
+/*
+ * acl__match_handlesFromCache
+ *
+ * We have 2 cacheed information
+ * 1) cached info from the previous operation
+ * 2) cached info from the prev entry evaluation
+ *
+ * What we are doing here is going thru all the acls and see if the same
+ * set of acls apply to this resource or not. If it does, then do we have
+ * a cached info for the attr. If we don't for both of the cases then we need
+ * to evaluate all over again.
+ *
+ * Inputs:
+ * struct acl_pblock - ACL private block;
+ * char *attr - Attribute name
+ * int access - acces type
+ *
+ * returns:
+ * LDAP_SUCCESS (0) - The same acls apply and we have
+ * access ALLOWED on the attr
+ * LDAP_INSUFFICIENT_ACCESS - The same acls apply and we have
+ * access DENIED on the attr
+ * -1 - Acls doesn't match or we don't have
+ * cached info for this attr.
+ *
+ * ASSUMPTIONS: A reader lock has been obtained for the acl list.
+ */
+static int
+acl__match_handlesFromCache ( Acl_PBlock *aclpb, char *attr, int access)
+{
+
+ aclEvalContext *c_evalContext = NULL;
+ int context_type = 0;
+ int ret_val = -1; /* it doen't match by default */
+
+ /* Before we proceed, find out if we have evaluated any ATTR RULE. If we have
+ ** then we can't use any caching mechanism
+ */
+
+ if ( aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ context_type = ACLPB_EVALCONTEXT_ACLCB;
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ } else {
+ context_type = ACLPB_EVALCONTEXT_PREV;
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ }
+
+
+ if ( aclpb->aclpb_res_type & (ACLPB_NEW_ENTRY | ACLPB_EFFECTIVE_RIGHTS) ) {
+ aclpb->aclpb_state |= ACLPB_MATCHES_ALL_ACLS;
+ ret_val = acl__scan_match_handles ( aclpb, context_type );
+ if ( -1 == ret_val ) {
+ aclpb->aclpb_state &= ~ACLPB_MATCHES_ALL_ACLS;
+ aclpb->aclpb_state |= ACLPB_UPD_ACLCB_CACHE;
+ /* Did not match */
+ if ( context_type == ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ aclpb->aclpb_state &= ~ACLPB_HAS_ACLCB_EVALCONTEXT;
+ } else {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ c_evalContext->acle_numof_tmatched_handles = 0;
+ }
+ }
+ }
+ if ( aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+ /* See if we have a cached result for this attr */
+ ret_val = acl__attr_cached_result (aclpb, attr, access);
+
+ /* It's not in the ACLCB context but we might have it in the
+ ** current/prev context. Take a look at it. we might have evaluated
+ ** this attribute already.
+ */
+ if ( (-1 == ret_val ) &&
+ ( aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT )) {
+ aclpb->aclpb_state &= ~ACLPB_HAS_ACLCB_EVALCONTEXT ;
+ ret_val = acl__attr_cached_result (aclpb, attr, access);
+ aclpb->aclpb_state |= ACLPB_HAS_ACLCB_EVALCONTEXT ;
+
+ /* We need to do an incremental update */
+ if ( !ret_val ) aclpb->aclpb_state |= ACLPB_INCR_ACLCB_CACHE;
+ }
+ }
+ return ret_val;
+}
+/*
+ * acl__get_attrEval
+ * Get the atteval from the current context and hold the ptr in aclpb.
+ * If we have too many attrs, then allocate a new one. In that case
+ * we let the caller know about that so that it will be deallocated.
+ *
+ * Returns:
+ * int - 0: The context was indexed. So, no allocations.
+ * - 1; context was allocated - deallocate it.
+ */
+static int
+acl__get_attrEval ( struct acl_pblock *aclpb, char *attr )
+{
+
+ int j;
+ aclEvalContext *c_ContextEval = &aclpb->aclpb_curr_entryEval_context;
+ int deallocate_attrEval = 0;
+ AclAttrEval *c_attrEval = NULL;
+
+ if ( !attr ) return deallocate_attrEval;
+
+ aclpb->aclpb_curr_attrEval = NULL;
+
+ /* Go thru and see if we have the attr already */
+ for (j=0; j < c_ContextEval->acle_numof_attrs; j++) {
+ c_attrEval = &c_ContextEval->acle_attrEval[j];
+
+ if ( c_attrEval &&
+ slapi_attr_type_cmp ( c_attrEval->attrEval_name, attr, 1) == 0 ) {
+ aclpb->aclpb_curr_attrEval = c_attrEval;
+ break;
+ }
+ }
+
+ if ( !aclpb->aclpb_curr_attrEval) {
+ if ( c_ContextEval->acle_numof_attrs == ACLPB_MAX_ATTRS -1 ) {
+ /* Too many attrs. create a temp one */
+ c_attrEval = (AclAttrEval * ) slapi_ch_calloc ( 1, sizeof ( AclAttrEval ) );
+ deallocate_attrEval =1;
+ } else {
+ c_attrEval = &c_ContextEval->acle_attrEval[c_ContextEval->acle_numof_attrs++];
+ c_attrEval->attrEval_r_status = 0;
+ c_attrEval->attrEval_s_status = 0;
+ c_attrEval->attrEval_r_aciIndex = 0;
+ c_attrEval->attrEval_s_aciIndex = 0;
+ }
+ /* clean it before use */
+ c_attrEval->attrEval_name = slapi_ch_strdup ( attr );
+ aclpb->aclpb_curr_attrEval = c_attrEval;
+ }
+ return deallocate_attrEval;
+}
+/*
+ * acl_skip_access_check
+ *
+ * See if we need to go thru the ACL check or not. We don't need to if I am root
+ * or internal operation or ...
+ *
+ * returns:
+ * ACL_TRUE - Yes; skip the ACL check
+ * ACL_FALSE - No; you have to go thru ACL check
+ *
+ */
+int
+acl_skip_access_check ( Slapi_PBlock *pb, Slapi_Entry *e )
+{
+ int rv, isRoot, accessCheckDisabled;
+ void *conn = NULL;
+ Slapi_Backend *be;
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+ if ( isRoot ) return ACL_TRUE;
+
+ /* See if this is local request */
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn);
+
+ if ( NULL == conn ) return ACL_TRUE;
+
+ /*
+ * Turn on access checking in the rootdse--this code used
+ * to skip the access check.
+ *
+ * check if the entry is the RootDSE entry
+ if ( e ) {
+ char * edn = slapi_entry_get_ndn ( e );
+ if ( slapi_is_rootdse ( edn ) ) return ACL_TRUE;
+ }
+ */
+
+ /* GB : when referrals are directly set in the mappin tree
+ * we can reach this code without a backend in the pblock
+ * in such a case, allow access for now
+ * we may want to reconsider this is NULL DSE implementation happens
+ */
+ rv = slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ if (be == NULL)
+ return ACL_TRUE;
+
+ rv = slapi_pblock_get ( pb, SLAPI_PLUGIN_DB_NO_ACL, &accessCheckDisabled );
+ if ( rv != -1 && accessCheckDisabled ) return ACL_TRUE;
+
+ return ACL_FALSE;
+}
+short
+acl_get_aclsignature ()
+{
+ return acl_signature;
+}
+void
+acl_set_aclsignature ( short value)
+{
+ acl_signature = value;
+}
+void
+acl_regen_aclsignature ()
+{
+ acl_signature = aclutil_gen_signature ( acl_signature );
+}
+
+
+
+static int
+acl__handle_config_entry (Slapi_Entry *e, void *callback_data )
+{
+
+ int *value = (int *) callback_data;
+
+ *value = slapi_entry_attr_get_int( e, "nsslapd-readonly");
+
+ return 0;
+}
+
+static int
+acl__config_get_readonly ()
+{
+
+ int readonly = 0;
+
+ slapi_search_internal_callback( "cn=config", LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL, 0 /* attrsonly */,
+ &readonly/* callback_data */,
+ NULL /* controls */,
+ NULL /* result_callback */,
+ acl__handle_config_entry,
+ NULL /* referral_callback */);
+
+ return readonly;
+}
+/*
+*
+* Assumptions:
+* 1) Called for read/search right.
+*/
+static int
+acl__recompute_acl ( Acl_PBlock *aclpb,
+ AclAttrEval *a_eval,
+ int access,
+ int aciIndex
+ )
+{
+
+
+ char *unused_str1, *unused_str2;
+ char *acl_tag, *testRight[2];
+ int j, expr_num;
+ int result_status, rv, cache_result;
+ PRUint32 cookie;
+ aci_t *aci;
+
+
+ PR_ASSERT ( aciIndex >= 0 );
+ PR_ASSERT ( a_eval != NULL );
+ PR_ASSERT (aclpb != NULL );
+
+
+ /* We might have evaluated this acl just now, check it there first */
+
+ for ( j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (aciIndex == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+ result_status =ACL_RES_INVALID;
+
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+
+
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if ((result & ACLPB_CACHE_SEARCH_RES_ALLOW) ||
+ (result & ACLPB_CACHE_READ_RES_ALLOW) )
+ result_status = ACL_RES_ALLOW;
+ else if ((result & ACLPB_CACHE_SEARCH_RES_DENY) ||
+ (result & ACLPB_CACHE_READ_RES_DENY) )
+ result_status = ACL_RES_DENY;
+
+ }
+ } /* end of for */
+
+ if ( result_status != ACL_RES_INVALID ) {
+ goto set_result_status;
+ }
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Recomputing the ACL Index:%d for entry:%s\n",
+ aciIndex, slapi_entry_get_ndn ( aclpb->aclpb_curr_entry) );
+
+ /* First find this one ACL and then evaluate it. */
+
+
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+ while ( aci && aci->aci_index != aciIndex ) {
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ }
+
+ if (NULL == aci)
+ return -1;
+
+
+ ACL_SetDefaultResult (NULL, aclpb->aclpb_acleval, ACL_RES_INVALID);
+ rv = ACL_EvalSetACL(NULL, aclpb->aclpb_acleval, aci->aci_handle);
+
+ testRight[0] = acl_access2str ( access );
+ testRight[1] = '\0';
+ aclpb->aclpb_curr_aci = aci;
+ result_status = ACL_EvalTestRights (NULL, aclpb->aclpb_acleval, testRight,
+ ds_map_generic, &unused_str1,
+ &unused_str2,
+ &acl_tag, &expr_num);
+
+ cache_result = 0;
+ if ( result_status == ACL_RES_DENY && aci->aci_type & ACI_HAS_DENY_RULE ) {
+ if ( access & SLAPI_ACL_SEARCH)
+ cache_result = ACLPB_CACHE_SEARCH_RES_DENY;
+ else
+ cache_result = ACLPB_CACHE_READ_RES_DENY;
+ } else if ( result_status == ACL_RES_ALLOW && aci->aci_type & ACI_HAS_ALLOW_RULE ) {
+ if ( access & SLAPI_ACL_SEARCH)
+ cache_result = ACLPB_CACHE_SEARCH_RES_ALLOW;
+ else
+ cache_result = ACLPB_CACHE_READ_RES_ALLOW;
+
+ } else {
+ result_status = -1;
+ }
+
+ /* Now we need to put the cached result in the aclpb */
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (aciIndex == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS-1) {
+ /* rbyrneXXX: make this same as other last_cache_result code! */
+ j = ++aclpb->aclpb_last_cache_result;
+ aclpb->aclpb_cache_result[j].aci_index = aci->aci_index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+
+ } else { /* No more space */
+ goto set_result_status;
+ }
+
+ /* Add the cached result status */
+ aclpb->aclpb_cache_result[j].result |= cache_result;
+
+
+
+set_result_status:
+ if (result_status == ACL_RES_ALLOW) {
+ if (access & SLAPI_ACL_SEARCH)
+ /*wrong bit maskes were being used here--
+ a_eval->attrEval_s_status = ACLPB_CACHE_SEARCH_RES_ALLOW;*/
+ a_eval->attrEval_s_status = ACL_ATTREVAL_SUCCESS;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_SUCCESS;
+
+ } else if ( result_status == ACL_RES_DENY) {
+ if (access & SLAPI_ACL_SEARCH)
+ a_eval->attrEval_s_status = ACL_ATTREVAL_FAIL;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_FAIL;
+ } else {
+ /* Here, set it to recompute--try again later */
+ if (access & SLAPI_ACL_SEARCH)
+ a_eval->attrEval_s_status = ACL_ATTREVAL_RECOMPUTE;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_RECOMPUTE;
+ result_status = -1;
+ }
+
+ return result_status;
+}
+
+static void
+__acl_set_aclIndex_inResult ( Acl_PBlock *aclpb, int access, int index )
+{
+ AclAttrEval *c_attrEval = aclpb->aclpb_curr_attrEval;
+
+ if ( c_attrEval ) {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_aciIndex = index;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_aciIndex = index;
+ }
+}
+
+/*
+ * If filter_sense is true then return (entry satisfies f).
+ * Otherwise, return !(entry satisfies f)
+*/
+
+static int
+acl__test_filter ( Slapi_Entry *entry, struct slapi_filter *f, int filter_sense) {
+ int filter_matched;
+
+ /* slapi_vattr_filter_test() returns 0 for match */
+
+ filter_matched = !slapi_vattr_filter_test(NULL, entry,
+ f,
+ 0 /*don't do acess chk*/);
+
+ if (filter_sense) {
+ return(filter_matched ? ACL_TRUE : ACL_FALSE);
+ } else {
+ return(filter_matched ? ACL_FALSE: ACL_TRUE);
+ }
+}
+
+/*
+ * Make an entry consisting of attr_type and attr_val and put
+ * a pointer to it in *entry.
+ * We will use this afterwards to test for against a filter.
+*/
+
+static int
+acl__make_filter_test_entry ( Slapi_Entry **entry, char *attr_type,
+ struct berval *attr_val) {
+
+ struct berval *vals_array[2];
+
+ vals_array[0] = attr_val;
+ vals_array[1] = NULL;
+
+ *entry = slapi_entry_alloc();
+ slapi_entry_init(*entry, NULL, NULL);
+
+ return (slapi_entry_add_values( *entry, (const char *)attr_type,
+ (struct berval**)&vals_array[0] ));
+
+}
+
+/*********************************************************************************/
+/* E N D */
+/*********************************************************************************/
diff --git a/ldap/servers/plugins/acl/acl.h b/ldap/servers/plugins/acl/acl.h
new file mode 100644
index 00000000..5e16a0bd
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl.h
@@ -0,0 +1,867 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*****************************************************************************
+* acl.h
+*
+* Header file for ACL processing
+*
+*****************************************************************************/
+#ifndef _ACL_H_
+#define _ACL_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include <ldap.h>
+#include <las.h>
+#include <aclproto.h>
+#include <aclerror.h>
+#include "prcvar.h"
+#include "slapi-plugin.h"
+#include "slap.h"
+#include "slapi-private.h"
+#include "portable.h"
+#include "prrwlock.h"
+#include "avl.h"
+
+#include "cert.h"
+
+#include <plhash.h>
+
+#ifdef SOLARIS
+ #include <tnf/probe.h>
+#else
+ #define TNF_PROBE_0_DEBUG(a,b,c)
+ #define TNF_PROBE_1_DEBUG(a,b,c,d,e,f)
+#endif
+
+#define ACL_PLUGIN_NAME "NSACLPlugin"
+extern char *plugin_name;
+
+/*
+ * Define the OID for version 2 of the proxied authorization control if
+ * it is not already defined (it is in recent copies of ldap.h).
+ */
+#ifndef LDAP_CONTROL_PROXIEDAUTH
+#define LDAP_CONTROL_PROXIEDAUTH "2.16.840.1.113730.3.4.18"
+#endif
+
+#define ACLUCHP unsigned char *
+
+static char* const aci_attr_type = "aci";
+static char* const filter_string = "aci=*";
+static char* const aci_targetdn = "target";
+static char* const aci_targetattr = "targetattr";
+static char* const aci_targetattrfilters = "targattrfilters";
+static char* const aci_targetfilter = "targetfilter";
+
+static char* const LDAP_URL_prefix = "ldap:///";
+
+static char* const access_str_compare = "compare";
+static char* const access_str_search = "search";
+static char* const access_str_read = "read";
+static char* const access_str_write = "write";
+static char* const access_str_delete = "delete";
+static char* const access_str_add = "add";
+static char* const access_str_selfwrite = "selfwrite";
+static char* const access_str_proxy = "proxy";
+
+#define ACL_INIT_ATTR_ARRAY 5
+
+/* define the method */
+#define DS_METHOD "ds_method"
+
+#define ACL_ESCAPE_STRING_WITH_PUNCTUATION(x,y) (slapi_is_loglevel_set(SLAPI_LOG_ACL) ? escape_string_with_punctuation(x,y) : "")
+
+/* Lases */
+#define DS_LAS_USER "user"
+#define DS_LAS_GROUP "group"
+#define DS_LAS_USERDN "userdn"
+#define DS_LAS_GROUPDN "groupdn"
+#define DS_LAS_USERDNATTR "userdnattr"
+#define DS_LAS_AUTHMETHOD "authmethod"
+#define DS_LAS_GROUPDNATTR "groupdnattr"
+#define DS_LAS_USERATTR "userattr"
+#define DS_LAS_ROLEDN "roledn"
+#define DS_LAS_ROLEDNATTR "rolednattr"
+
+
+/* These define the things that aclutil_evaluate_macro() supports */
+typedef enum
+{
+ ACL_EVAL_USER,
+ ACL_EVAL_GROUP,
+ ACL_EVAL_ROLE,
+ ACL_EVAL_GROUPDNATTR,
+ ACL_EVAL_TARGET_FILTER
+}acl_eval_types;
+
+typedef enum
+{
+ ACL_RULE_MACRO_DN_TYPE,
+ ACL_RULE_MACRO_DN_LEVELS_TYPE
+}acl_rule_macro_types;
+
+#define ACL_TARGET_MACRO_DN_KEY "($dn)"
+#define ACL_RULE_MACRO_DN_KEY "($dn)"
+#define ACL_RULE_MACRO_DN_LEVELS_KEY "[$dn]"
+#define ACL_RULE_MACRO_ATTR_KEY "($attr."
+
+#define ACL_EVAL_USER 0
+#define ACL_EVAL_GROUP 1
+#define ACL_EVAL_ROLE 2
+
+/* The LASes are implemented in the libaccess library */
+#define DS_LAS_TIMEOFDAY "timeofday"
+#define DS_LAS_DAYOFWEEK "dayofweek"
+
+
+/* ACL function return codes */
+#define ACL_TRUE 1 /* evaluation results to TRUE */
+#define ACL_OK ACL_TRUE
+#define ACL_FALSE 0 /* evaluation results to FALSE */
+#define ACL_ERR -1 /* generic error */
+#define ACL_TARGET_FILTER_ERR -2 /* Target filter not set properly */
+#define ACL_TARGETATTR_FILTER_ERR -3 /* TargetAttr filter not set properly */
+#define ACL_TARGETFILTER_ERR -4 /* Target filter not set properly */
+#define ACL_SYNTAX_ERR -5 /* Syntax error */
+#define ACL_ONEACL_TEXT_ERR -6 /* ONE ACL text error */
+#define ACL_ERR_CONCAT_HANDLES -7 /* unable to concat the handles */
+#define ACL_INVALID_TARGET -8 /* invalid target */
+#define ACL_INVALID_AUTHMETHOD -9 /* multiple client auth */
+#define ACL_INVALID_AUTHORIZATION -10 /* no authorization */
+#define ACL_INCORRECT_ACI_VERSION -11 /* incorrect version # */
+#define ACL_DONT_KNOW -12 /* the world is an uncertain place */
+
+/* supported by the DS */
+#define DS_PROP_CONNECTION "connection"
+#define DS_ATTR_USERDN "userdn"
+#define DS_ATTR_ENTRY "entry"
+#define DS_PROP_ACLPB "aclblock"
+#define DS_ATTR_AUTHTYPE "authtype"
+#define DS_ATTR_CERT "clientcert"
+
+#define ACL_ANOM_MAX_ACL 40
+struct scoped_entry_anominfo {
+ short anom_e_targetInfo[ACL_ANOM_MAX_ACL];
+ short anom_e_nummatched;
+ short anom_e_isrootds;
+};
+
+typedef struct targetattr {
+ int attr_type;
+#define ACL_ATTR_FILTER 0x01
+#define ACL_ATTR_STRING 0x02
+#define ACL_ATTR_STAR 0x04 /* attr is * only */
+
+ union {
+ char *attr_str;
+ struct slapi_filter *attr_filter;
+ }u;
+}Targetattr;
+
+typedef struct targetattrfilter {
+ char *attr_str;
+ char *filterStr;
+ struct slapi_filter *filter; /* value filter */
+
+}Targetattrfilter;
+
+typedef struct Aci_Macro {
+ char *match_this;
+ char *macro_ptr; /* ptr into match_this */
+}aciMacro;
+
+typedef PLHashTable acl_ht_t;
+
+/* Access Control Item (aci): Stores information about a particular ACL */
+typedef struct aci {
+ int aci_type; /* Type of resurce */
+
+/* THE FIRST BYTE WAS USED TO KEEP THE RIGHTS. ITS BEEN MOVED TO
+** aci_access and is now free.
+**
+**
+**
+*/
+
+#define ACI_TARGET_MACRO_DN (int)0x000001
+#define ACI_TARGET_FILTER_MACRO_DN (int)0x000002
+#define ACI_TARGET_DN (int)0x000100 /* target has DN */
+#define ACI_TARGET_ATTR (int)0x000200 /* target is an attr */
+#define ACI_TARGET_PATTERN (int)0x000400 /* target has some patt */
+#define ACI_TARGET_FILTER (int)0x000800 /* target has a filter */
+#define ACI_ACLTXT (int)0x001000 /* ACI has text only */
+#define ACI_TARGET_NOT (int)0x002000 /* it's a != */
+#define ACI_TARGET_ATTR_NOT (int)0x004000 /* It's a != manager */
+#define ACI_TARGET_FILTER_NOT (int)0x008000 /* It's a != filter */
+#define ACI_UNUSED2 (int)0x010000 /* Unused */
+#define ACI_HAS_ALLOW_RULE (int)0x020000 /* allow (...) */
+#define ACI_HAS_DENY_RULE (int)0x040000 /* deny (...) */
+#define ACI_CONTAIN_NOT_USERDN (int)0x080000 /* userdn != blah */
+#define ACI_TARGET_ATTR_ADD_FILTERS (int)0x100000
+#define ACI_TARGET_ATTR_DEL_FILTERS (int)0x200000
+#define ACI_CONTAIN_NOT_GROUPDN (int)0x400000 /* groupdn != blah */
+#define ACI_CONTAIN_NOT_ROLEDN (int)0x800000
+
+ int aci_access;
+
+/*
+ * See also aclpb_access which is used to store rights too.
+*/
+
+ short aci_ruleType; /* kinds of rules in the ACL */
+
+#define ACI_USERDN_RULE (short) 0x0001
+#define ACI_USERDNATTR_RULE (short) 0x0002
+#define ACI_GROUPDN_RULE (short) 0x0004
+#define ACI_GROUPDNATTR_RULE (short) 0x0008
+#define ACI_AUTHMETHOD_RULE (short) 0x0010
+#define ACI_IP_RULE (short) 0x0020
+#define ACI_DNS_RULE (short) 0x0040
+#define ACI_TIMEOFDAY_RULE (short) 0x0080
+#define ACI_DAYOFWEEK_RULE (short) 0x0010
+#define ACI_USERATTR_RULE (short) 0x0200
+/*
+ * These are extension of USERDN/GROUPDN rule. However since the
+ * semantics are quite different, we classify them as different rules.
+ * ex: groupdn = "ldap:///cn=helpdesk, ou=$attr.dept, o=$dn.o, o=isp"
+ */
+#define ACI_PARAM_DNRULE (short) 0x0400
+#define ACI_PARAM_ATTRRULE (short) 0x0800
+#define ACI_USERDN_SELFRULE (short) 0x1000
+#define ACI_ROLEDN_RULE (short) 0x2000
+
+
+
+#define ACI_ATTR_RULES ( ACI_USERDNATTR_RULE | ACI_GROUPDNATTR_RULE | ACI_USERATTR_RULE | ACI_PARAM_DNRULE | ACI_PARAM_ATTRRULE | ACI_USERDN_SELFRULE)
+#define ACI_CACHE_RESULT_PER_ENTRY ACI_ATTR_RULES
+
+ short aci_elevel; /* Based on the aci type some idea about the
+ ** execution flow
+ */
+ int aci_index; /* index # */
+ Slapi_DN *aci_sdn; /* location */
+ Slapi_Filter *target; /* Target is a DN */
+ Targetattr **targetAttr;
+ char *targetFilterStr;
+ struct slapi_filter *targetFilter; /* Target has a filter */
+ Targetattrfilter **targetAttrAddFilters;
+ Targetattrfilter **targetAttrDelFilters;
+ char *aclName; /* ACL name */
+ struct ACLListHandle *aci_handle; /*handle of the ACL */
+ aciMacro *aci_macro;
+ struct aci *aci_next; /* next one */
+}aci_t;
+
+/* Aci excution level
+** The idea is that for each handle types, we can prioritize which one to evaluate first.
+** Evaluating the user before the group is better.
+*/
+#define ACI_ELEVEL_USERDN_ANYONE 0
+#define ACI_ELEVEL_USERDN_ALL 1
+#define ACI_ELEVEL_USERDN 2
+#define ACI_ELEVEL_USERDNATTR 3
+#define ACI_ELEVEL_GROUPDNATTR_URL 4
+#define ACI_ELEVEL_GROUPDNATTR 5
+#define ACI_ELEVEL_GROUPDN 6
+#define ACI_MAX_ELEVEL ACI_ELEVEL_GROUPDN +1
+#define ACI_DEFAULT_ELEVEL ACI_MAX_ELEVEL
+
+
+#define ACLPB_MAX_SELECTED_ACLS 200
+
+typedef struct result_cache {
+ int aci_index;
+ short aci_ruleType;
+ short result;
+#define ACLPB_CACHE_READ_RES_ALLOW (short)0x0001 /* used for ALLOW handles only */
+#define ACLPB_CACHE_READ_RES_DENY (short)0x0002 /* used for DENY handles only */
+#define ACLPB_CACHE_SEARCH_RES_ALLOW (short)0x0004 /* used for ALLOW handles only */
+#define ACLPB_CACHE_SEARCH_RES_DENY (short)0x0008 /* used for DENY handles only */
+#define ACLPB_CACHE_SEARCH_RES_SKIP (short)0x0010 /* used for both types */
+#define ACLPB_CACHE_READ_RES_SKIP (short)0x0020 /* used for both types */
+}r_cache_t;
+#define ACLPB_MAX_CACHE_RESULTS ACLPB_MAX_SELECTED_ACLS
+
+/*
+ * This is use to keep the result of the evaluation of the attr.
+ * We are only intrested in read/searc only.
+ */
+struct acl_attrEval {
+ char *attrEval_name; /* Attribute Name */
+ short attrEval_r_status; /* status of read evaluation */
+ short attrEval_s_status; /* status of search evaluation */
+ int attrEval_r_aciIndex; /* Index of the ACL which grants access*/
+ int attrEval_s_aciIndex; /* Index of the ACL which grants access*/
+
+#define ACL_ATTREVAL_SUCCESS 0x1
+#define ACL_ATTREVAL_FAIL 0x2
+#define ACL_ATTREVAL_RECOMPUTE 0x4
+#define ACL_ATTREVAL_DETERMINISTIC 7
+#define ACL_ATTREVAL_INVALID 0x8
+
+};
+typedef struct acl_attrEval AclAttrEval;
+
+
+/*
+ * Struct to keep the evaluation context information. This struct is
+ * used in multiple places ( different instance ) to keep the context for
+ * current entry evaluation, previous entry evaluation or previous operation
+ * evaluation status.
+ */
+#define ACLPB_MAX_ATTR_LEN 100
+#define ACLPB_MAX_ATTRS 100
+struct acleval_context {
+
+ /* Information about the attrs */
+ AclAttrEval acle_attrEval[ACLPB_MAX_ATTRS];
+ short acle_numof_attrs;
+
+ /* Handles information */
+ short acle_numof_tmatched_handles;
+ int acle_handles_matched_target[ACLPB_MAX_SELECTED_ACLS];
+};
+typedef struct acleval_context aclEvalContext;
+
+
+struct acl_usergroup {
+ short aclug_signature;
+/*
+ * To modify refcnt you need either the write lock on the whole cache or
+ * the reader lock on the whole cache plus this refcnt mutex
+*/
+ short aclug_refcnt;
+ PRLock *aclug_refcnt_mutex;
+
+
+ char *aclug_ndn; /* Client's normalized DN */
+
+ char **aclug_member_groups;
+ short aclug_member_group_size;
+ short aclug_numof_member_group;
+
+ char **aclug_notmember_groups;
+ short aclug_notmember_group_size;
+ short aclug_numof_notmember_group;
+ struct acl_usergroup *aclug_next;
+ struct acl_usergroup *aclug_prev;
+
+};
+typedef struct acl_usergroup aclUserGroup;
+
+#define ACLUG_INCR_GROUPS_LIST 20
+
+struct aci_container {
+ Slapi_DN *acic_sdn; /* node DN */
+ aci_t *acic_list; /* List of the ACLs for that node */
+ int acic_index; /* index to the container array */
+};
+typedef struct aci_container AciContainer;
+
+struct acl_pblock {
+ int aclpb_state;
+
+#define ACLPB_ACCESS_ALLOWED_ON_A_ATTR 0x000001
+#define ACLPB_ACCESS_DENIED_ON_ALL_ATTRS 0x000002
+#define ACLPB_ACCESS_ALLOWED_ON_ENTRY 0x000004
+#define ACLPB_ATTR_STAR_MATCHED 0x000008
+#define ACLPB_FOUND_ATTR_RULE 0x000010
+#define ACLPB_SEARCH_BASED_ON_LIST 0x000020
+#define ACLPB_EXECUTING_DENY_HANDLES 0x000040
+#define ACLPB_EXECUTING_ALLOW_HANDLES 0x000080
+#define ACLPB_ACCESS_ALLOWED_USERATTR 0x000100
+#ifdef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+ #define ACLPB_USER_SPECIFIED_ATTARS 0x000200
+ #define ACLPB_USER_WANTS_ALL_ATTRS 0x000400
+#endif
+#define ACLPB_EVALUATING_FIRST_ATTR 0x000800
+#define ACLPB_FOUND_A_ENTRY_TEST_RULE 0x001000
+#define ACLPB_SEARCH_BASED_ON_ENTRY_LIST 0x002000
+#define ACLPB_DONOT_USE_CONTEXT_ACLS 0x004000
+#define ACLPB_HAS_ACLCB_EVALCONTEXT 0x008000
+#define ACLPB_COPY_EVALCONTEXT 0x010000
+#define ACLPB_MATCHES_ALL_ACLS 0x020000
+#define ACLPB_INITIALIZED 0x040000
+#define ACLPB_INCR_ACLCB_CACHE 0x080000
+#define ACLPB_UPD_ACLCB_CACHE 0x100000
+#define ACLPB_ATTR_RULE_EVALUATED 0x200000
+#define ACLPB_DONOT_EVALUATE_PROXY 0x400000
+
+
+#define ACLPB_RESET_MASK ( ACLPB_ACCESS_ALLOWED_ON_A_ATTR | ACLPB_ACCESS_DENIED_ON_ALL_ATTRS | \
+ ACLPB_ACCESS_ALLOWED_ON_ENTRY | ACLPB_ATTR_STAR_MATCHED | \
+ ACLPB_FOUND_ATTR_RULE | ACLPB_EVALUATING_FIRST_ATTR | \
+ ACLPB_FOUND_A_ENTRY_TEST_RULE )
+#define ACLPB_STATE_ALL 0x3fffff
+
+ int aclpb_res_type;
+
+ #define ACLPB_NEW_ENTRY 0x100
+ #define ACLPB_EFFECTIVE_RIGHTS 0x200
+ #define ACLPB_RESTYPE_ALL 0x7ff
+
+ /*
+ * The bottom bye used to be for rights. It's free now as they have
+ * been moved to aclpb_access.
+ */
+
+ int aclpb_access;
+
+#define ACLPB_SLAPI_ACL_WRITE_ADD 0x200
+#define ACLPB_SLAPI_ACL_WRITE_DEL 0x400
+
+ /* stores the requested access during an operation */
+
+ short aclpb_signature;
+ short aclpb_type;
+#define ACLPB_TYPE_MAIN 1
+#define ACLPB_TYPE_MAIN_STR "Main Block"
+#define ACLPB_TYPE_PROXY 2
+#define ACLPB_TYPE_PROXY_STR "Proxy Block"
+
+ Slapi_Entry *aclpb_client_entry; /* A copy of client's entry */
+ Slapi_PBlock *aclpb_pblock; /* back to LDAP PBlock */
+ int aclpb_optype; /* current optype from pb */
+
+ /* Current entry/dn/attr evaluation info */
+ Slapi_Entry *aclpb_curr_entry; /* current Entry being processed */
+ int aclpb_num_entries;
+ Slapi_DN *aclpb_curr_entry_sdn; /* Entry's SDN */
+ Slapi_DN *aclpb_authorization_sdn; /* dn used for authorization */
+
+ AclAttrEval *aclpb_curr_attrEval; /* Current attr being evaluated */
+ struct berval *aclpb_curr_attrVal; /* Value of Current attr */
+ Slapi_Entry *aclpb_filter_test_entry; /* Scratch entry */
+ aci_t *aclpb_curr_aci;
+ char *aclpb_Evalattr; /* The last attr evaluated */
+
+ /* Plist and eval info */
+ ACLEvalHandle_t *aclpb_acleval; /* acleval handle for evaluation */
+ struct PListStruct_s *aclpb_proplist;/* All the needed property */
+
+ /* DENY ACI HANDLES */
+ aci_t **aclpb_deny_handles;
+ int aclpb_deny_handles_size;
+ int aclpb_num_deny_handles;
+
+ /* ALLOW ACI HANDLES */
+ aci_t **aclpb_allow_handles;
+ int aclpb_allow_handles_size;
+ int aclpb_num_allow_handles;
+
+ /* This is used in the groupdnattr="URL" rule
+ ** Keep a list of base where searched has been done
+ */
+ char **aclpb_grpsearchbase;
+ int aclpb_grpsearchbase_size;
+ int aclpb_numof_bases;
+
+ aclUserGroup *aclpb_groupinfo;
+
+ /* Keep the Group nesting level */
+ int aclpb_max_nesting_level;
+ int aclpb_max_member_sizelimit;
+
+
+ /* To keep the results in the cache */
+
+ int aclpb_last_cache_result;
+ struct result_cache aclpb_cache_result[ACLPB_MAX_CACHE_RESULTS];
+
+ /* Index numbers of ACLs selected based on a locality search*/
+ char *aclpb_search_base;
+ int aclpb_base_handles_index[ACLPB_MAX_SELECTED_ACLS];
+ int aclpb_handles_index[ACLPB_MAX_SELECTED_ACLS];
+
+ /* Evaluation context info
+ ** 1) Context cached from aclcb ( from connection struct )
+ ** 2) Context cached from previous entry evaluation
+ ** 3) current entry evaluation info
+ */
+ aclEvalContext aclpb_curr_entryEval_context;
+ aclEvalContext aclpb_prev_entryEval_context;
+ aclEvalContext aclpb_prev_opEval_context;
+
+ /* Currentry anom profile sumamry */
+ struct scoped_entry_anominfo aclpb_scoped_entry_anominfo;
+
+ /* Some Statistics gathering */
+ PRUint16 aclpb_stat_acllist_scanned;
+ PRUint16 aclpb_stat_aclres_matched;
+ PRUint16 aclpb_stat_total_entries;
+ PRUint16 aclpb_stat_anom_list_scanned;
+ PRUint16 aclpb_stat_num_copycontext;
+ PRUint16 aclpb_stat_num_copy_attrs;
+ PRUint16 aclpb_stat_num_tmatched_acls;
+ PRUint16 aclpb_stat_unused;
+ CERTCertificate *aclpb_clientcert;
+ AciContainer *aclpb_aclContainer;
+ struct acl_pblock *aclpb_proxy; /* Child proxy block */
+ acl_ht_t *aclpb_macro_ht; /* ht for partial macro strs */
+
+ struct acl_pblock *aclpb_prev; /* Previpous in the chain */
+ struct acl_pblock *aclpb_next; /* Next in the chain */
+};
+typedef struct acl_pblock Acl_PBlock;
+
+/* PBLCOK TYPES */
+typedef enum
+{
+ ACLPB_BINDDN_PBLOCK,
+ ACLPB_PROXYDN_PBLOCK,
+ ACLPB_ALL_PBLOCK
+}aclpb_types;
+
+
+#define ACLPB_EVALCONTEXT_CURR 1
+#define ACLPB_EVALCONTEXT_PREV 2
+#define ACLPB_EVALCONTEXT_ACLCB 3
+
+
+
+/* Cleaning/ deallocating/ ... acl_freeBlock() */
+#define ACL_CLEAN_ACLPB 1
+#define ACL_COPY_ACLCB 2
+#define ACL_CLEAN_ACLCB 3
+
+/* used to differentiate acl plugins sharing the same lib */
+#define ACL_PLUGIN_IDENTITY 1
+#define ACL_PREOP_PLUGIN_IDENTITY 2
+
+
+/* start with 50 and then add 50 more as required
+ * The first ACI_MAX_ELEVEL slots are predefined.
+ */
+#define ACLPB_INCR_LIST_HANDLES ACI_MAX_ELEVEL + 43
+
+#define ACLPB_INCR_BASES 5
+
+/*
+ * acl private block which hangs from connection structure.
+ * This is allocated the first time an operation is done and freed when the
+ * connection are cleaned.
+ *
+ */
+struct acl_cblock {
+
+ short aclcb_aclsignature;
+ short aclcb_state;
+#define ACLCB_HAS_CACHED_EVALCONTEXT 0x1
+
+ Slapi_DN *aclcb_sdn; /* Contains bind SDN */
+ aclEvalContext aclcb_eval_context;
+ PRLock *aclcb_lock; /* shared lock */
+};
+
+struct acl_groupcache {
+ short aclg_state; /* status information */
+ short aclg_signature;
+ int aclg_num_userGroups;
+ aclUserGroup *aclg_first;
+ aclUserGroup *aclg_last;
+ PRRWLock *aclg_rwlock; /* lock to monitor the group cache */
+};
+typedef struct acl_groupcache aclGroupCache;
+
+
+/* Type of extensions that can be registered */
+typedef enum
+{
+ ACL_EXT_OPERATION, /* extension for Operation object */
+ ACL_EXT_CONNECTION, /* extension for Connection object */
+ ACL_EXT_ALL
+}ext_type;
+
+/* Used to pass data around in acllas.c */
+
+typedef struct {
+ char *clientDn;
+ char *authType;
+ int anomUser;
+ Acl_PBlock *aclpb;
+ Slapi_Entry *resourceEntry;
+
+}lasInfo;
+
+
+/* reasons why the subject allowed/denied access--good for logs */
+
+typedef enum{
+ACL_REASON_NO_ALLOWS,
+ACL_REASON_RESULT_CACHED_DENY,
+ACL_REASON_EVALUATED_DENY, /* evaluated deny */
+ACL_REASON_RESULT_CACHED_ALLOW, /* cached allow */
+ACL_REASON_EVALUATED_ALLOW, /* evalauted allow */
+ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS, /* were allows/denies, but none matched */
+ACL_REASON_NONE, /* no reason available */
+ACL_REASON_ANON_ALLOWED,
+ACL_REASON_ANON_DENIED,
+ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS,
+ACL_REASON_EVALCONTEXT_CACHED_ALLOW,
+ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED,
+ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW
+}aclReasonCode_t;
+
+typedef struct{
+ aci_t *deciding_aci;
+ aclReasonCode_t reason;
+}aclResultReason_t;
+#define ACL_NO_DECIDING_ACI_INDEX -10
+
+
+/* Extern declaration for backend state change fnc: acllist.c and aclinit.c */
+
+void acl_be_state_change_fnc ( void *handle, char *be_name, int old_state,
+ int new_state);
+
+
+/* Extern declaration for ATTRs */
+
+extern int
+DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASDnsGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASUserDnGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASGroupDnGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASEntryGetter(NSErr_t *errp, PList_t subject, PList_t resource,
+ PList_t auth_info, PList_t global_auth, void *arg);
+
+extern int
+DS_LASCertGetter(NSErr_t *errp, PList_t subject, PList_t resource,
+ PList_t auth_info, PList_t global_auth, void *arg);
+
+/* function declartion for LAses supported by DS */
+
+extern int DS_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);
+
+extern int DS_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 DS_LASUserDnEval(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 DS_LASGroupDnEval(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 DS_LASRoleDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASUserDnAttrEval(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 DS_LASAuthMethodEval(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 DS_LASGroupDnAttrEval(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 DS_LASRoleDnAttrEval(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 DS_LASUserAttrEval(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);
+
+/* other function declaration */
+int aclinit_main();
+int acl_match_substring (struct slapi_filter *f, char *str, int match);
+void acl_print_acllib_err(NSErr_t *errp, char * str);
+void acl_initBlock ( Slapi_PBlock *pb );
+void acl_freeBlock ( Slapi_PBlock *pb, int state );
+int acl_read_access_allowed_on_entry ( Slapi_PBlock *pb, Slapi_Entry *e,
+ char **attrs, int access);
+int acl_access_allowed_modrdn ( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access);
+int acl_read_access_allowed_on_attr ( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access);
+void acl_set_acllist (Slapi_PBlock *pb, int scope, char *base);
+void acl_gen_err_msg(int access, char *edn, char *attr, char **errbuf);
+void acl_modified ( Slapi_PBlock *pb, int optype, char *dn, void *change);
+int acl_access_allowed_disjoint_resource( Slapi_PBlock *pb, Slapi_Entry *e,
+ char *attr, struct berval *val, int access );
+int acl_access_allowed_main ( Slapi_PBlock *pb, Slapi_Entry *e, char **attrs,
+ struct berval *val, int access , int flags, char **errbuf);
+int acl_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access );
+int acl_verify_syntax(const Slapi_DN *e_sdn, const struct berval *bval);
+aclUserGroup * acl_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn);
+void acl_print_acllib_err (NSErr_t *errp , char * str);
+int acl_check_mods( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf );
+int acl_verify_aci_syntax (Slapi_Entry *e, char **errbuf);
+char * acl__access2str(int access);
+void acl_strcpy_special (char *d, char *s);
+int acl_parse(char * str, aci_t *aci_item);
+char * acl_access2str ( int access );
+int acl_init_ext ();
+void * acl_get_ext (ext_type type, void *object);
+void acl_set_ext (ext_type type, void *object, void *data);
+void acl_reset_ext_status (ext_type type, void *object);
+void acl_init_op_ext ( Slapi_PBlock *pb , int type, char *dn, int copy);
+void * acl_operation_ext_constructor (void *object, void *parent );
+void acl_operation_ext_destructor ( void *ext, void *object, void *parent );
+void * acl_conn_ext_constructor (void *object, void *parent );
+void acl_conn_ext_destructor ( void *ext, void *object, void *parent );
+void acl_clean_aclEval_context ( aclEvalContext *clean_me, int scrub_only );
+void acl_copyEval_context ( struct acl_pblock *aclpb, aclEvalContext *src,
+ aclEvalContext *dest , int copy_attr_only );
+struct acl_pblock * acl_get_aclpb ( Slapi_PBlock *pb, int type );
+int acl_client_anonymous ( Slapi_PBlock *pb );
+short acl_get_aclsignature();
+void acl_set_aclsignature( short value);
+void acl_regen_aclsignature();
+struct acl_pblock * acl_new_proxy_aclpb( Slapi_PBlock *pb );
+void acl_set_authorization_dn( Slapi_PBlock *pb, char *dn, int type );
+int acl_get_proxyauth_dn( Slapi_PBlock *pb, char **proxydnp,
+ char **errtextp );
+void acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb,
+ const char *dn, int copy_from_aclcb);
+int acl_create_aclpb_pool ();
+int acl_skip_access_check ( Slapi_PBlock *pb, Slapi_Entry *e );
+
+int aclext_alloc_lockarray ();
+
+int aclutil_str_appened(char **str1, const char *str2);
+void aclutil_print_err (int rv , const Slapi_DN *sdn,
+ const struct berval* val, char **errbuf);
+void aclutil_print_aci (aci_t *aci_item, char *type);
+short aclutil_gen_signature ( short c_signature );
+void aclutil_print_resource( struct acl_pblock *aclpb, char *right , char *attr, char *clientdn );
+char * aclutil_expand_paramString ( char *str, Slapi_Entry *e );
+
+
+void acllist_init_scan (Slapi_PBlock *pb, int scope, char *base);
+aci_t * acllist_get_first_aci (Acl_PBlock *aclpb, PRUint32 *cookie );
+aci_t * acllist_get_next_aci ( Acl_PBlock *aclpb, aci_t *curraci, PRUint32 *cookie );
+aci_t * acllist_get_aci_new ();
+void acllist_free_aci (aci_t *item);
+void acllist_acicache_READ_UNLOCK(void);
+void acllist_acicache_READ_LOCK(void);
+void acllist_acicache_WRITE_UNLOCK(void);
+void acllist_acicache_WRITE_LOCK(void);
+void acllist_aciscan_update_scan ( Acl_PBlock *aclpb, char *edn );
+int acllist_remove_aci_needsLock( const Slapi_DN *sdn, const struct berval *attr );
+int acllist_insert_aci_needsLock( const Slapi_DN *e_sdn, const struct berval* aci_attr);
+int acllist_init ();
+int acllist_moddn_aci_needsLock ( Slapi_DN *oldsdn, char *newdn );
+void acllist_print_tree ( Avlnode *root, int *depth, char *start, char *side);
+AciContainer *acllist_get_aciContainer_new ( );
+void acllist_done_aciContainer ( AciContainer *);
+
+aclUserGroup* aclg_find_userGroup (char *n_dn);
+void aclg_regen_ugroup_signature( aclUserGroup *ugroup);
+void aclg_markUgroupForRemoval ( aclUserGroup *u_group );
+void aclg_reader_incr_ugroup_refcnt(aclUserGroup* u_group);
+int aclg_numof_usergroups(void);
+int aclgroup_init ();
+void aclg_regen_group_signature ();
+void aclg_reset_userGroup ( struct acl_pblock *aclpb );
+void aclg_init_userGroup ( struct acl_pblock *aclpb, const char *dn , int got_lock);
+aclUserGroup * aclg_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn);
+
+void aclg_lock_groupCache (int type );
+void aclg_unlock_groupCache (int type );
+
+int aclanom_init();
+int aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb,
+ Slapi_Entry *e, char *attr, int access);
+void aclanom_get_suffix_info(Slapi_Entry *e, struct acl_pblock *aclpb );
+void aclanom_invalidateProfile();
+typedef enum{
+ DONT_TAKE_ACLCACHE_READLOCK,
+ DO_TAKE_ACLCACHE_READLOCK,
+ DONT_TAKE_ACLCACHE_WRITELOCK,
+ DO_TAKE_ACLCACHE_WRITELOCK
+}acl_lock_flag_t;
+void aclanom_gen_anomProfile (acl_lock_flag_t lock_flag);
+int aclanom_is_client_anonymous ( Slapi_PBlock *pb );
+int aclinit_main ();
+typedef struct aclinit_handler_callback_data {
+#define ACL_ADD_ACIS 1
+#define ACL_REMOVE_ACIS 0
+ int op;
+ int retCode;
+ acl_lock_flag_t lock_flag;
+}aclinit_handler_callback_data_t;
+int
+aclinit_search_and_update_aci ( int thisbeonly, const Slapi_DN *base,
+ char *be_name, int scope, int op,
+ acl_lock_flag_t lock_flag);
+void *aclplugin_get_identity(int plug);
+int
+acl_dn_component_match( const char *ndn, char *match_this, int component_number);
+char *
+acl_match_macro_in_target( const char *ndn, char *match_this,
+ char *macro_ptr);
+char* get_next_component(char *dn, int *index);
+int acl_match_prefix( char *macro_prefix, const char *ndn,
+ int *exact_match);
+char *
+get_this_component(char *dn, int *index);
+int
+acl_find_comp_end( char * s);
+char *
+acl_replace_str(char * s, char *substr, char* replace_with);
+int acl_strstr(char * s, char *substr);
+int aclutil_evaluate_macro( char * rule, lasInfo *lasinfo,
+ acl_eval_types evalType );
+
+/* acl hash table functions */
+void acl_ht_add_and_freeOld(acl_ht_t * acl_ht, PLHashNumber key,char *value);
+acl_ht_t *acl_ht_new(void);
+void acl_ht_free_all_entries_and_values( acl_ht_t *acl_ht);
+void acl_ht_remove( acl_ht_t *acl_ht, PLHashNumber key);
+void *acl_ht_lookup( acl_ht_t *acl_ht, PLHashNumber key);
+void acl_ht_display_ht( acl_ht_t *acl_ht);
+
+/* acl get effective rights */
+int
+acl_get_effective_rights ( Slapi_PBlock *pb, Slapi_Entry *e,
+ char **attrs, struct berval *val, int access, char **errbuf );
+
+char* aclutil__access_str (int type , char str[]);
+
+#endif /* _ACL_H_ */
diff --git a/ldap/servers/plugins/acl/acl_ext.c b/ldap/servers/plugins/acl/acl_ext.c
new file mode 100644
index 00000000..28a9ce18
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl_ext.c
@@ -0,0 +1,968 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+static void acl__done_aclpb ( struct acl_pblock *aclpb );
+static void acl__dump_stats ( struct acl_pblock *aclpb , const char *block_type);
+static Acl_PBlock * acl__get_aclpb_from_pool ( );
+static int acl__put_aclpb_back_to_pool ( Acl_PBlock *aclpb );
+static Acl_PBlock * acl__malloc_aclpb ( );
+static char * acl__get_aclpb_type ( Acl_PBlock *aclpb );
+static PRLock *aclext_get_lock ();
+
+
+struct acl_pbqueue {
+ Acl_PBlock *aclq_free;
+ Acl_PBlock *aclq_busy;
+ short aclq_nfree;
+ short aclq_nbusy;
+ PRLock *aclq_lock;
+};
+typedef struct acl_pbqueue Acl_PBqueue;
+
+static Acl_PBqueue *aclQueue;
+
+/* structure with information for each extension */
+typedef struct acl_ext
+{
+ char *object_name; /* name of the object extended */
+ int object_type; /* handle to the extended object */
+ int handle; /* extension handle */
+} acl_ext;
+
+static acl_ext acl_ext_list [ACL_EXT_ALL];
+
+/*
+ * EXTENSION INITIALIZATION, CONSTRUCTION, & DESTRUCTION
+ *
+ */
+int
+acl_init_ext ()
+{
+ int rc;
+
+ acl_ext_list[ACL_EXT_OPERATION].object_name = SLAPI_EXT_OPERATION;
+
+ rc = slapi_register_object_extension(plugin_name, SLAPI_EXT_OPERATION,
+ acl_operation_ext_constructor,
+ acl_operation_ext_destructor,
+ &acl_ext_list[ACL_EXT_OPERATION].object_type,
+ &acl_ext_list[ACL_EXT_OPERATION].handle);
+
+ if ( rc != 0 ) return rc;
+
+ acl_ext_list[ACL_EXT_CONNECTION].object_name = SLAPI_EXT_CONNECTION;
+ rc = slapi_register_object_extension(plugin_name, SLAPI_EXT_CONNECTION,
+ acl_conn_ext_constructor,
+ acl_conn_ext_destructor,
+ &acl_ext_list[ACL_EXT_CONNECTION].object_type,
+ &acl_ext_list[ACL_EXT_CONNECTION].handle);
+
+ return rc;
+
+}
+
+/* Interface to get the extensions */
+void *
+acl_get_ext (ext_type type, void *object)
+{
+ struct acl_ext ext;
+ void *data;
+
+ if ( type >= ACL_EXT_ALL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Invalid extension type:%d\n", type );
+ return NULL;
+ }
+
+ /* find the requested extension */
+ ext = acl_ext_list [type];
+ data = slapi_get_object_extension(ext.object_type, object, ext.handle);
+
+ return data;
+}
+
+void
+acl_set_ext (ext_type type, void *object, void *data)
+{
+ if ( type >= 0 && type < ACL_EXT_ALL )
+ {
+ struct acl_ext ext = acl_ext_list [type];
+ slapi_set_object_extension ( ext.object_type, object, ext.handle, data );
+ }
+}
+
+/****************************************************************************
+ * Global lock array so that private extension between connection and operation
+ * co-exist
+ *
+ ******************************************************************************/
+struct ext_lockArray {
+ PRLock **lockArray;
+ int numlocks;
+};
+
+static struct ext_lockArray extLockArray;
+
+/* PKBxxx: make this a configurable. Start with 2 * maxThreads */
+#define ACLEXT_MAX_LOCKS 40
+
+int
+aclext_alloc_lockarray ( )
+{
+
+ int i;
+ PRLock *lock;
+
+ extLockArray.lockArray =
+ (PRLock **) slapi_ch_calloc ( ACLEXT_MAX_LOCKS, sizeof ( PRLock *) );
+
+ for ( i =0; i < ACLEXT_MAX_LOCKS; i++) {
+ if (NULL == (lock = PR_NewLock()) ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate locks used for private extension\n");
+ return 1;
+ }
+ extLockArray.lockArray[i] = lock;
+ }
+ extLockArray.numlocks = ACLEXT_MAX_LOCKS;
+ return 0;
+}
+static PRUint32 slot_id =0;
+static PRLock *
+aclext_get_lock ()
+{
+
+ PRUint16 slot = slot_id % ACLEXT_MAX_LOCKS;
+ slot_id++;
+ return ( extLockArray.lockArray[slot] );
+
+}
+/****************************************************************************/
+/* CONNECTION EXTENSION SPECIFIC */
+/****************************************************************************/
+void *
+acl_conn_ext_constructor ( void *object, void *parent )
+{
+ struct acl_cblock *ext = NULL;
+
+ ext = (struct acl_cblock * ) slapi_ch_calloc (1, sizeof (struct acl_cblock ) );
+ if (( ext->aclcb_lock = aclext_get_lock () ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to get Read/Write lock for CONNECTION extension\n");
+ slapi_ch_free ( (void **) &ext );
+ return NULL;
+ }
+ ext->aclcb_sdn = slapi_sdn_new ();
+ /* store the signatures */
+ ext->aclcb_aclsignature = acl_get_aclsignature();
+ ext->aclcb_state = -1;
+ return ext;
+
+
+}
+
+void
+acl_conn_ext_destructor ( void *ext, void *object, void *parent )
+{
+ struct acl_cblock *aclcb = ext;
+ PRLock *shared_lock;
+
+ if ( NULL == aclcb ) return;
+ PR_Lock ( aclcb->aclcb_lock );
+ shared_lock = aclcb->aclcb_lock;
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /* clean*/ );
+ slapi_sdn_free ( &aclcb->aclcb_sdn );
+ aclcb->aclcb_lock = NULL;
+ slapi_ch_free ( (void **) &aclcb );
+
+ PR_Unlock ( shared_lock );
+}
+
+/****************************************************************************/
+/* OPERATION EXTENSION SPECIFIC */
+/****************************************************************************/
+void *
+acl_operation_ext_constructor ( void *object, void *parent )
+{
+ Acl_PBlock *aclpb = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_constructor_start ,"ACL","");
+
+ /* This means internal operations */
+ if ( NULL == parent) {
+
+ TNF_PROBE_1_DEBUG(acl_operation_ext_constructor_end ,"ACL","",
+ tnf_string,internal_op,"");
+
+ return NULL;
+ }
+
+ aclpb = acl__get_aclpb_from_pool();
+ if ( NULL == aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Operation extension allocation Failed\n");
+ }
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_constructor_end ,"ACL","");
+
+ return aclpb;
+
+}
+
+void
+acl_operation_ext_destructor ( void *ext, void *object, void *parent )
+{
+
+ struct acl_cblock *aclcb = NULL;
+ struct acl_pblock *aclpb = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_destructor_start ,"ACL","");
+
+ if ( (NULL == parent ) || (NULL == ext)) {
+ TNF_PROBE_1_DEBUG(acl_operation_ext_destructor_end ,"ACL","",
+ tnf_string,internal_op,"");
+
+ return;
+ }
+
+ aclpb = (Acl_PBlock *) ext;
+
+ if ( (NULL == aclpb) ||
+ (NULL == aclpb->aclpb_pblock) ||
+ (!(aclpb->aclpb_state & ACLPB_INITIALIZED)))
+ goto clean_aclpb;
+
+ /* get the connection extension */
+ aclcb = (struct acl_cblock *) acl_get_ext ( ACL_EXT_CONNECTION, parent );
+
+ /* We are about to get out of this connection. Move all the
+ ** cached information to the acl private block which hangs
+ ** from the connection struct.
+ */
+ if ( aclcb && aclcb->aclcb_lock &&
+ ( (aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE ) ||
+ (aclpb->aclpb_state & ACLPB_INCR_ACLCB_CACHE ) ) ) {
+
+ aclEvalContext *c_evalContext;
+ int attr_only = 0;
+ PRLock *shared_lock = aclcb->aclcb_lock;
+
+ if (aclcb->aclcb_lock ) PR_Lock ( shared_lock );
+ else {
+ goto clean_aclpb;
+ }
+ if ( !aclcb->aclcb_lock ) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "aclcb lock released! aclcb cache can't be refreshed\n");
+ PR_Unlock ( shared_lock );
+ goto clean_aclpb;
+ }
+
+ /* We need to refresh the aclcb cache */
+ if ( aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE )
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /* clean*/ );
+ if ( aclpb->aclpb_prev_entryEval_context.acle_numof_attrs ) {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ } else {
+ c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+ }
+
+ if (( aclpb->aclpb_state & ACLPB_INCR_ACLCB_CACHE ) &&
+ ! ( aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE ))
+ attr_only = 1;
+
+ acl_copyEval_context ( NULL, c_evalContext, &aclcb->aclcb_eval_context, attr_only );
+
+ aclcb->aclcb_aclsignature = aclpb->aclpb_signature;
+ if ( aclcb->aclcb_sdn && aclpb->aclpb_authorization_sdn &&
+ (0 != slapi_sdn_compare ( aclcb->aclcb_sdn,
+ aclpb->aclpb_authorization_sdn ) ) ) {
+ slapi_sdn_set_ndn_byval( aclcb->aclcb_sdn,
+ slapi_sdn_get_ndn ( aclpb->aclpb_authorization_sdn ) );
+ }
+ aclcb->aclcb_state = 0;
+ aclcb->aclcb_state |= ACLCB_HAS_CACHED_EVALCONTEXT;
+
+ PR_Unlock ( shared_lock );
+ }
+
+clean_aclpb:
+ if ( aclpb ) {
+
+ if ( aclpb->aclpb_proxy ) {
+ TNF_PROBE_0_DEBUG(acl_proxy_aclpbdoneback_start ,"ACL","");
+
+ acl__done_aclpb( aclpb->aclpb_proxy );
+
+ /* Put back to the Pool */
+ acl__put_aclpb_back_to_pool ( aclpb->aclpb_proxy );
+ aclpb->aclpb_proxy = NULL;
+ TNF_PROBE_0_DEBUG(acl_proxy_aclpbdoneback_end ,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(acl_aclpbdoneback_start ,"ACL","");
+
+ acl__done_aclpb( aclpb);
+ acl__put_aclpb_back_to_pool ( aclpb );
+
+ TNF_PROBE_0_DEBUG(acl_aclpbdoneback_end ,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_destructor_end ,"ACL","");
+
+}
+
+/****************************************************************************/
+/* FUNCTIONS TO MANAGE THE ACLPB POOL */
+/****************************************************************************/
+
+/*
+ * Get the right acl pblock
+ */
+struct acl_pblock *
+acl_get_aclpb ( Slapi_PBlock *pb, int type )
+{
+ Acl_PBlock *aclpb = NULL;
+ void *op = NULL;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ aclpb = (Acl_PBlock *) acl_get_ext ( ACL_EXT_OPERATION, op );
+ if (NULL == aclpb ) return NULL;
+
+ if ( type == ACLPB_BINDDN_PBLOCK )
+ return aclpb;
+ else if ( type == ACLPB_PROXYDN_PBLOCK )
+ return aclpb->aclpb_proxy;
+ else
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "acl_get_aclpb: Invalid aclpb type %d\n", type );
+ return NULL;
+}
+/*
+ * Create a new proxy acl pblock
+ *
+ */
+struct acl_pblock *
+acl_new_proxy_aclpb( Slapi_PBlock *pb )
+{
+ void *op;
+ Acl_PBlock *aclpb = NULL;
+ Acl_PBlock *proxy_aclpb = NULL;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ aclpb = (Acl_PBlock *) acl_get_ext ( ACL_EXT_OPERATION, op );
+ if (NULL == aclpb ) return NULL;
+
+ proxy_aclpb = acl__get_aclpb_from_pool();
+ if (NULL == proxy_aclpb) return NULL;
+ proxy_aclpb->aclpb_type = ACLPB_TYPE_PROXY;
+
+ aclpb->aclpb_proxy = proxy_aclpb;
+
+ return proxy_aclpb;
+
+}
+static int
+acl__handle_config_entry (Slapi_Entry *e, void *callback_data )
+{
+ *(int * )callback_data = slapi_entry_attr_get_int( e, "nsslapd-threadnumber");
+
+ return 0;
+}
+
+/*
+ * Create a pool of acl pblock. Created during the ACL plugin
+ * initialization.
+ */
+int
+acl_create_aclpb_pool ()
+{
+
+ Acl_PBlock *aclpb;
+ Acl_PBlock *prev_aclpb;
+ Acl_PBlock *first_aclpb;
+ int i;
+ int maxThreads= 0;
+
+ slapi_search_internal_callback( "cn=config", LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL, 0 /* attrsonly */,
+ &maxThreads/* callback_data */,
+ NULL /* controls */,
+ NULL /* result_callback */,
+ acl__handle_config_entry,
+ NULL /* referral_callback */);
+
+ /* Create a pool pf aclpb */
+ maxThreads = 2 * maxThreads;
+
+ aclQueue = ( Acl_PBqueue *) slapi_ch_calloc ( 1, sizeof (Acl_PBqueue) );
+ aclQueue->aclq_lock = PR_NewLock();
+
+ if ( NULL == aclQueue->aclq_lock ) {
+ /* ERROR */
+ return 1;
+ }
+
+ prev_aclpb = NULL;
+ first_aclpb = NULL;
+ for ( i = 0; i < maxThreads; i++ ) {
+ aclpb = acl__malloc_aclpb ();
+ if ( 0 == i) first_aclpb = aclpb;
+
+ aclpb->aclpb_prev = prev_aclpb;
+ if ( prev_aclpb ) prev_aclpb->aclpb_next = aclpb;
+ prev_aclpb = aclpb;
+ }
+
+ /* Since this is the begining, everybody is in free list */
+ aclQueue->aclq_free = first_aclpb;
+
+ aclQueue->aclq_nfree = maxThreads;
+ return 0;
+}
+
+/*
+ * Get a FREE acl pblock from the pool.
+ *
+ */
+static Acl_PBlock *
+acl__get_aclpb_from_pool ( )
+{
+ Acl_PBlock *aclpb = NULL;
+ Acl_PBlock *t_aclpb = NULL;
+
+
+ PR_Lock (aclQueue->aclq_lock );
+
+ /* Get the first aclpb from the FREE List */
+ aclpb = aclQueue->aclq_free;
+ if ( aclpb ) {
+ t_aclpb = aclpb->aclpb_next;
+ if ( t_aclpb ) t_aclpb->aclpb_prev = NULL;
+ aclQueue->aclq_free = t_aclpb;
+
+ /* make the this an orphon */
+ aclpb->aclpb_prev = aclpb->aclpb_next = NULL;
+
+ aclQueue->aclq_nfree--;
+ } else {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Unable to find a free aclpb\n");
+ aclpb = acl__malloc_aclpb ();
+ }
+
+
+ /* Now move it to the FRONT of busy list */
+ t_aclpb = aclQueue->aclq_busy;
+ aclpb->aclpb_next = t_aclpb;
+ if ( t_aclpb ) t_aclpb->aclpb_prev = aclpb;
+ aclQueue->aclq_busy = aclpb;
+ aclQueue->aclq_nbusy++;
+
+ PR_Unlock (aclQueue->aclq_lock );
+
+ return aclpb;
+}
+/*
+ * Put the acl pblock into the FREE pool.
+ *
+ */
+static int
+acl__put_aclpb_back_to_pool ( Acl_PBlock *aclpb )
+{
+
+ Acl_PBlock *p_aclpb, *n_aclpb;
+
+ PR_Lock (aclQueue->aclq_lock );
+
+ /* Remove it from the busy list */
+ n_aclpb = aclpb->aclpb_next;
+ p_aclpb = aclpb->aclpb_prev;
+
+ if ( p_aclpb ) {
+ p_aclpb->aclpb_next = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = p_aclpb;
+ } else {
+ aclQueue->aclq_busy = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = NULL;
+ }
+ aclQueue->aclq_nbusy--;
+
+
+ /* Put back to the FREE list */
+ aclpb->aclpb_prev = NULL;
+ n_aclpb = aclQueue->aclq_free;
+ aclpb->aclpb_next = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = aclpb;
+ aclQueue->aclq_free = aclpb;
+ aclQueue->aclq_nfree++;
+
+ PR_Unlock (aclQueue->aclq_lock );
+
+ return 0;
+}
+
+/*
+ * Allocate the basic acl pb
+ *
+ */
+static Acl_PBlock *
+acl__malloc_aclpb ( )
+{
+ Acl_PBlock *aclpb = NULL;
+
+
+ aclpb = ( Acl_PBlock *) slapi_ch_calloc ( 1, sizeof ( Acl_PBlock) );
+
+ /* Now set the propert we need for ACL evaluations */
+ if ((aclpb->aclpb_proplist = PListNew(NULL)) == NULL) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate the aclprop PList\n");
+ return NULL;
+ }
+
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_PROP_ACLPB, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the ACL PBLOCK in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_USERDN, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the USER DN in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_AUTHTYPE, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the AUTH TYPE in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_ENTRY, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the ENTRY TYPE in the Plist\n");
+ return NULL;
+ }
+
+ /*
+ * ACL_ATTR_IP and ACL_ATTR_DNS are initialized lazily in the
+ * IpGetter and DnsGetter functions.
+ * They are removed from the aclpb property list at acl__aclpb_done()
+ * time.
+ */
+
+ /* allocate the acleval struct */
+ aclpb->aclpb_acleval = (ACLEvalHandle_t *) ACL_EvalNew(NULL, NULL);
+ if (aclpb->aclpb_acleval == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate the acleval block\n");
+ return NULL;
+ }
+ /*
+ * This is a libaccess routine.
+ * Need to setup subject and resource property information
+ */
+
+ ACL_EvalSetSubject(NULL, aclpb->aclpb_acleval, aclpb->aclpb_proplist);
+
+ /* allocate some space for attr name */
+ aclpb->aclpb_Evalattr = (char *) slapi_ch_malloc (ACLPB_MAX_ATTR_LEN);
+
+ aclpb->aclpb_deny_handles = (aci_t **) slapi_ch_calloc (1,
+ ACLPB_INCR_LIST_HANDLES * sizeof (aci_t *));
+
+ aclpb->aclpb_allow_handles = (aci_t **) slapi_ch_calloc (1,
+ ACLPB_INCR_LIST_HANDLES * sizeof (aci_t *));
+
+ aclpb->aclpb_deny_handles_size = ACLPB_INCR_LIST_HANDLES;
+ aclpb->aclpb_allow_handles_size = ACLPB_INCR_LIST_HANDLES;
+
+ /* allocate the array for bases */
+ aclpb->aclpb_grpsearchbase = (char **)
+ slapi_ch_malloc (ACLPB_INCR_BASES * sizeof(char *));
+ aclpb->aclpb_grpsearchbase_size = ACLPB_INCR_BASES;
+ aclpb->aclpb_numof_bases = 0;
+
+ /* Make sure aclpb_search_base is initialized to NULL..tested elsewhere! */
+ aclpb->aclpb_search_base = NULL;
+
+ aclpb->aclpb_authorization_sdn = slapi_sdn_new ();
+ aclpb->aclpb_curr_entry_sdn = slapi_sdn_new();
+
+ aclpb->aclpb_aclContainer = acllist_get_aciContainer_new ();
+
+ /* hash table to store macro matched values from targets */
+ aclpb->aclpb_macro_ht = acl_ht_new();
+
+ return aclpb;
+
+}
+
+/* Initializes the aclpb */
+void
+acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb, const char *dn, int copy_from_aclcb)
+{
+ struct acl_cblock *aclcb = NULL;
+ char *authType;
+ void *conn;
+ unsigned long op_type;
+
+
+ if ( NULL == aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "acl_init_aclpb:No ACLPB\n");
+ return;
+ }
+
+ /* See if we have initialized already */
+ if (aclpb->aclpb_state & ACLPB_INITIALIZED) return;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION_TYPE, &op_type );
+ if ( op_type == SLAPI_OPERATION_BIND || op_type == SLAPI_OPERATION_UNBIND )
+ return;
+
+ /* We indicate the initialize here becuase, if something goes wrong, it's cleaned up
+ ** properly.
+ */
+ aclpb->aclpb_state = ACLPB_INITIALIZED;
+
+ /* We make an anonymous user a non null dn which is empty */
+ if (dn && *dn != '\0' )
+ slapi_sdn_set_ndn_byval ( aclpb->aclpb_authorization_sdn, dn );
+ else
+ slapi_sdn_set_ndn_byval ( aclpb->aclpb_authorization_sdn, "" );
+
+ /* reset scoped entry cache to be empty */
+ aclpb->aclpb_scoped_entry_anominfo.anom_e_nummatched = 0;
+
+ if (PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_USERDN,
+ slapi_sdn_get_ndn(aclpb->aclpb_authorization_sdn), 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the USER DN in the Plist\n");
+ return;
+ }
+ slapi_pblock_get ( pb, SLAPI_OPERATION_AUTHTYPE, &authType );
+ if (PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_AUTHTYPE, authType, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the AUTH TYPE in the Plist\n");
+ return;
+ }
+ /* PKBxxx: We should be getting it from the OP struct */
+ slapi_pblock_get ( pb, SLAPI_CONN_CERT, &aclpb->aclpb_clientcert );
+
+ /* See if the we have already a cached info about user's group */
+ aclg_init_userGroup ( aclpb, dn, 0 /* get lock */ );
+
+ slapi_pblock_get( pb, SLAPI_BE_MAXNESTLEVEL, &aclpb->aclpb_max_nesting_level );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &aclpb->aclpb_max_member_sizelimit );
+ if ( aclpb->aclpb_max_member_sizelimit == 0 ) {
+ aclpb->aclpb_max_member_sizelimit = SLAPD_DEFAULT_LOOKTHROUGHLIMIT;
+ }
+ slapi_pblock_get( pb, SLAPI_OPERATION_TYPE, &aclpb->aclpb_optype );
+
+ aclpb->aclpb_signature = acl_get_aclsignature();
+ aclpb->aclpb_last_cache_result = 0;
+ aclpb->aclpb_pblock = pb;
+ PR_ASSERT ( aclpb->aclpb_pblock != NULL );
+
+ /* get the connection */
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn);
+ aclcb = (struct acl_cblock *) acl_get_ext ( ACL_EXT_CONNECTION, conn );
+
+ if (NULL == aclcb || NULL == aclcb->aclcb_lock) {
+ /* This could happen if the client is dead and we are in
+ ** process of abondoning this operation
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "No CONNECTION extension\n");
+
+ } else if ( aclcb->aclcb_state == -1 ) {
+ /* indicate that we need to update the cache */
+ aclpb->aclpb_state |= ACLPB_UPD_ACLCB_CACHE;
+ aclcb->aclcb_state = 0; /* Nore this is ACLCB and not ACLPB */
+
+ } else if ( copy_from_aclcb ){
+ char *cdn;
+ Slapi_DN *c_sdn; /* client SDN */
+
+ /* check if the operation is abandoned or not.*/
+ if ( slapi_op_abandoned ( pb ) ) {
+ return;
+ }
+
+ slapi_pblock_get ( pb, SLAPI_CONN_DN, &cdn ); /* We *must* free cdn! */
+ c_sdn = slapi_sdn_new_dn_passin( cdn );
+ PR_Lock ( aclcb->aclcb_lock );
+ /*
+ * since PR_Lock is taken,
+ * we can mark the connection extension ok to be destroyed.
+ */
+ if ( (aclcb->aclcb_aclsignature != acl_get_aclsignature()) ||
+ ( (NULL == cdn) && aclcb->aclcb_sdn ) ||
+ (cdn && (NULL == aclcb->aclcb_sdn )) ||
+ (cdn && aclcb->aclcb_sdn && ( 0 != slapi_sdn_compare ( c_sdn, aclcb->aclcb_sdn ) ))) {
+
+ /* cleanup the aclcb cache */
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /*clean*/ );
+ aclcb->aclcb_state = 0;
+ aclcb->aclcb_aclsignature = 0;
+ slapi_sdn_done ( aclcb->aclcb_sdn );
+ }
+ slapi_sdn_free ( &c_sdn );
+
+ /* COPY the cached information from ACLCB --> ACLPB */
+ if ( aclcb->aclcb_state & ACLCB_HAS_CACHED_EVALCONTEXT) {
+ acl_copyEval_context ( aclpb, &aclcb->aclcb_eval_context ,
+ &aclpb->aclpb_prev_opEval_context, 0 );
+ aclpb->aclpb_state |= ACLPB_HAS_ACLCB_EVALCONTEXT;
+ }
+ PR_Unlock ( aclcb->aclcb_lock );
+ }
+
+}
+
+/* Cleans up the aclpb */
+static void
+acl__done_aclpb ( struct acl_pblock *aclpb )
+{
+
+ int i;
+ int dump_aclpb_info = 0;
+ char *ds_attr_userdn=NULL; /* for finding userdn for freeing */
+ int rc=-1;
+ char *tmp_ptr=NULL;
+
+ /*
+ ** First, let's do some sanity checks to see if we have everything what
+ ** it should be.
+ */
+
+ /* Nothing needs to be cleaned up in this case */
+ if ( !aclpb->aclpb_state & ACLPB_INITIALIZED)
+ return;
+
+ /* Check the state */
+ if (aclpb->aclpb_state & ~ACLPB_STATE_ALL) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "The aclpb.state value (%d) is incorrect. Exceeded the limit (%d)\n",
+ aclpb->aclpb_state, ACLPB_STATE_ALL);
+ dump_aclpb_info = 1;
+
+ }
+
+ /* acl__dump_stats ( aclpb, acl__get_aclpb_type(aclpb)); */
+
+ /* reset the usergroup cache */
+ aclg_reset_userGroup ( aclpb );
+
+ if ( aclpb->aclpb_res_type & ~ACLPB_RESTYPE_ALL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "The aclpb res_type value (%d) has exceeded. Limit is (%d)\n",
+ aclpb->aclpb_res_type, ACLPB_RESTYPE_ALL, 0 );
+ dump_aclpb_info = 1;
+ }
+
+ if ( dump_aclpb_info ) {
+ const char *ndn;
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "ACLPB value is:%p\n", aclpb, 0,0 );
+
+ ndn = slapi_sdn_get_ndn ( aclpb->aclpb_curr_entry_sdn );
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "curr_entry:%p num_entries:%d curr_dn:%p\n",
+ aclpb->aclpb_curr_entry ? (char *) aclpb->aclpb_curr_entry : "NULL",
+ aclpb->aclpb_num_entries,
+ ndn ? ndn : "NULL");
+
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Last attr:%p, Plist:%p acleval: %p\n",
+ aclpb->aclpb_Evalattr ? aclpb->aclpb_Evalattr : "NULL",
+ aclpb->aclpb_proplist ? (char *) aclpb->aclpb_proplist : "NULL",
+ aclpb->aclpb_acleval ? (char *) aclpb->aclpb_acleval : "NULL" );
+ }
+
+ /* Now Free the contents or clean it */
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+ if (aclpb->aclpb_Evalattr)
+ aclpb->aclpb_Evalattr[0] = '\0';
+
+ /* deallocate the contents of the base array */
+ for (i=0; i < aclpb->aclpb_numof_bases; i++) {
+ if (aclpb->aclpb_grpsearchbase[i])
+ slapi_ch_free ( (void **)&aclpb->aclpb_grpsearchbase[i] );
+ }
+ aclpb->aclpb_numof_bases = 0;
+
+ acl_clean_aclEval_context ( &aclpb->aclpb_prev_opEval_context, 0 /*claen*/ );
+ acl_clean_aclEval_context ( &aclpb->aclpb_prev_entryEval_context, 0 /*clean*/ );
+ acl_clean_aclEval_context ( &aclpb->aclpb_curr_entryEval_context, 0/*clean*/ );
+
+ if ( aclpb->aclpb_client_entry ) slapi_entry_free ( aclpb->aclpb_client_entry );
+ aclpb->aclpb_client_entry = NULL;
+
+ slapi_sdn_done ( aclpb->aclpb_authorization_sdn );
+ aclpb->aclpb_pblock = NULL;
+
+ if ( aclpb->aclpb_search_base )
+ slapi_ch_free ( (void **) &aclpb->aclpb_search_base );
+ for ( i=0; i < aclpb->aclpb_num_deny_handles; i++ )
+ aclpb->aclpb_deny_handles[i] = NULL;
+ aclpb->aclpb_num_deny_handles = 0;
+
+ for ( i=0; i < aclpb->aclpb_num_allow_handles; i++ )
+ aclpb->aclpb_allow_handles[i] = NULL;
+ aclpb->aclpb_num_allow_handles = 0;
+
+ /* clear results cache */
+ memset((char*)aclpb->aclpb_cache_result, 0,
+ sizeof(struct result_cache)*aclpb->aclpb_last_cache_result);
+ aclpb->aclpb_last_cache_result = 0;
+ aclpb->aclpb_handles_index[0] = -1;
+ aclpb->aclpb_base_handles_index[0] = -1;
+
+ aclpb->aclpb_stat_acllist_scanned = 0;
+ aclpb->aclpb_stat_aclres_matched = 0;
+ aclpb->aclpb_stat_total_entries = 0;
+ aclpb->aclpb_stat_anom_list_scanned = 0;
+ aclpb->aclpb_stat_num_copycontext = 0;
+ aclpb->aclpb_stat_num_copy_attrs = 0;
+ aclpb->aclpb_stat_num_tmatched_acls = 0;
+
+ aclpb->aclpb_clientcert = NULL;
+ aclpb->aclpb_proxy = NULL;
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer );
+
+ /*
+ * Here, decide which things need to be freed/removed/whatever from the
+ * aclpb_proplist.
+ */
+
+ /*
+ * The DS_ATTR_DNS property contains the name of the client machine.
+ *
+ * The value pointed to by this property is stored in the pblock--it
+ * points to the SLAPI_CLIENT_DNS object. So, that memory will
+ * be freed elsewhere.
+ *
+ * It's removed here from the aclpb_proplist as it would be an error to
+ * allow it to persist in the aclpb which is an operation time thing.
+ * If we leave it here the next time this aclpb gets used, the DnsGetter
+ * is not called by LASDnsEval/ACL_GetAttribute() as it thinks the
+ * ACL_ATTR_DNS has already been initialized.
+ *
+ */
+
+ if ((rc = PListFindValue(aclpb->aclpb_proplist, ACL_ATTR_DNS,
+ (void **)&tmp_ptr, NULL)) > 0) {
+
+ PListDeleteProp(aclpb->aclpb_proplist, rc, NULL);
+ }
+
+ /*
+ * Remove the DS_ATTR_IP property from the property list.
+ * The value of this property is just the property pointer
+ * (an unsigned long) so that gets freed too when we delete the
+ * property.
+ * It's removed here from the aclpb_proplist as it would be an error to
+ * allow it to persist in the aclpb which is an operation time thing.
+ * If we leave it here the next time this aclpb gets used, the DnsGetter
+ * is not called by LASIpEval/ACL_GetAttribute() as it thinks the
+ * ACL_ATTR_IP has already been initialized.
+ */
+
+ if ((rc = PListFindValue(aclpb->aclpb_proplist, ACL_ATTR_IP,
+ (void **)&tmp_ptr, NULL)) > 0) {
+
+ PListDeleteProp(aclpb->aclpb_proplist, rc, NULL);
+ }
+
+ /*
+ * The DS_ATTR_USERDN value comes from aclpb_authorization_sdn.
+ * This memory
+ * is freed above using aclpb_authorization_sdn so we don't need to free it here
+ * before overwriting the old value.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_USERDN, NULL, 0);
+
+ /*
+ * The DS_ATTR_AUTHTYPE value is a pointer into the pblock, so
+ * we do not need to free that memory before overwriting the value.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_AUTHTYPE, NULL, 0);
+
+ /*
+ * DO NOT overwrite the aclpb pointer--it is initialized at malloc_aclpb
+ * time and is kept within the aclpb.
+ *
+ * PListAssignValue(aclpb->aclpb_proplist, DS_PROP_ACLPB, NULL, 0);
+ */
+
+ /*
+ * The DS_ATTR_ENTRY value was a pointer to the entry being evaluated
+ * by the ACL code. That entry comes from outside the context of
+ * the acl code and so is dealt with out there. Ergo, here we can just
+ * lose the pointer to that entry.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_ENTRY, NULL, 0);
+
+ aclpb->aclpb_signature = 0;
+
+ /* reset scoped entry cache to be empty */
+ aclpb->aclpb_scoped_entry_anominfo.anom_e_nummatched = 0;
+
+ /* Free up any of the string values left in the macro ht and remove
+ * the entries.*/
+ acl_ht_free_all_entries_and_values(aclpb->aclpb_macro_ht);
+
+ /* Finally, set it to the no use state */
+ aclpb->aclpb_state = 0;
+
+}
+
+static char *
+acl__get_aclpb_type ( Acl_PBlock *aclpb )
+{
+
+ if (aclpb->aclpb_state & ACLPB_TYPE_PROXY)
+ return ACLPB_TYPE_PROXY_STR;
+
+ return ACLPB_TYPE_MAIN_STR;
+}
+static void
+acl__dump_stats ( struct acl_pblock *aclpb , const char *block_type)
+{
+ int connid = 0;
+ int opid = 0;
+ Slapi_PBlock *pb = NULL;
+
+ pb = aclpb->aclpb_pblock;
+ if ( pb ) {
+ slapi_pblock_get ( pb, SLAPI_CONN_ID, &connid );
+ slapi_pblock_get ( pb, SLAPI_OPERATION_ID, &opid );
+ }
+
+ /* DUMP STAT INFO */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "**** ACL OPERATION STAT BEGIN ( aclpb:%p Block type: %s): Conn:%d Operation:%d *******\n",
+ aclpb, block_type, connid, opid );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of entries scanned: %d\n",
+ aclpb->aclpb_stat_total_entries);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times ACL List scanned: %d\n",
+ aclpb->aclpb_stat_acllist_scanned);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of ACLs with target matched:%d\n",
+ aclpb->aclpb_stat_num_tmatched_acls);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times acl resource matched:%d\n",
+ aclpb->aclpb_stat_aclres_matched);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times ANOM list scanned:%d\n",
+ aclpb->aclpb_stat_anom_list_scanned);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times Context was copied:%d\n",
+ aclpb->aclpb_stat_num_copycontext);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times Attrs was copied:%d\n",
+ aclpb->aclpb_stat_num_copy_attrs);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, " **** ACL OPERATION STAT END *******\n");
+}
+/****************************************************************************/
+/* E N D */
+/****************************************************************************/
+
diff --git a/ldap/servers/plugins/acl/aclanom.c b/ldap/servers/plugins/acl/aclanom.c
new file mode 100644
index 00000000..71b0c68a
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclanom.c
@@ -0,0 +1,536 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/************************************************************************
+Anonymous profile
+**************************************************************************/
+
+struct anom_targetacl {
+ int anom_type; /* defines for anom types same as aci_type */
+ int anom_access;
+ Slapi_DN *anom_target; /* target of the ACL */
+ Slapi_Filter *anom_filter; /* targetfilter part */
+ char **anom_targetAttrs; /* list of attrs */
+};
+
+
+struct anom_profile {
+ short anom_signature;
+ short anom_numacls;
+ struct anom_targetacl anom_targetinfo[ACL_ANOM_MAX_ACL];
+};
+
+static struct anom_profile *acl_anom_profile = NULL;
+
+static PRRWLock *anom_rwlock = NULL;
+#define ANOM_LOCK_READ() PR_RWLock_Rlock (anom_rwlock )
+#define ANOM_UNLOCK_READ() PR_RWLock_Unlock (anom_rwlock )
+#define ANOM_LOCK_WRITE() PR_RWLock_Wlock (anom_rwlock )
+#define ANOM_UNLOCK_WRITE() PR_RWLock_Unlock (anom_rwlock )
+
+
+static void __aclanom__del_profile ();
+
+/*
+ * aclanom_init ();
+ * Generate a profile for the anonymous user. We can use this profile
+ * later to determine what resources the client is allowed to.
+ *
+ * Dependency:
+ * Before calling this, it is assumed that all the ACLs have been read
+ * and parsed.
+ *
+ * We will go thru all the ACL and pick the ANYONE ACL and generate the anom
+ * profile.
+ *
+ */
+int
+aclanom_init ()
+{
+
+ acl_anom_profile = (struct anom_profile * )
+ slapi_ch_calloc (1, sizeof ( struct anom_profile ) );
+
+ if (( anom_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"ANOM LOCK") ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Failed in getting the ANOM rwlock\n" );
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Depending on the context, this routine may need to take the
+ * acicache read lock.
+*/
+void
+aclanom_gen_anomProfile (acl_lock_flag_t lock_flag)
+{
+ aci_t *aci = NULL;
+ int i;
+ Targetattr **srcattrArray;
+ Targetattr *attr;
+ struct anom_profile *a_profile;
+ PRUint32 cookie;
+
+ PR_ASSERT( lock_flag == DO_TAKE_ACLCACHE_READLOCK ||
+ lock_flag == DONT_TAKE_ACLCACHE_READLOCK);
+
+ /*
+ * This routine requires two locks:
+ * the one for the global cache in acllist_acicache_READ_LOCK() and
+ * the one for the anom profile.
+ * They _must_ be taken in the order presented here or there
+ * is a deadlock scenario with acllist_remove_aci_needsLock() which
+ * takes them is this order.
+ */
+
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_LOCK();
+ }
+ ANOM_LOCK_WRITE ();
+ a_profile = acl_anom_profile;
+
+ if ( (!acl_get_aclsignature()) || ( !a_profile) ||
+ (a_profile->anom_signature == acl_get_aclsignature()) ) {
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+ return;
+ }
+
+ /* D0 we have one already. If we do, then clean it up */
+ __aclanom__del_profile();
+
+ /* We have a new signature now */
+ a_profile->anom_signature = acl_get_aclsignature();
+
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "GENERATING ANOM USER PROFILE\n", 0,0,0);
+ /*
+ ** Go thru the ACL list and find all the ACLs which apply to the
+ ** anonymous user i.e anyone. we can generate a profile for that.
+ ** We will llok at the simple case i.e it matches
+ ** cases not handled:
+ ** 1) When there is a mix if rule types ( allows & denies )
+ **
+ */
+
+ aci = acllist_get_first_aci ( NULL, &cookie );
+ while ( aci ) {
+ int a_numacl;
+ struct slapi_filter *f;
+ char **destattrArray;
+
+
+ /*
+ * We must not have a rule like: deny ( all ) userdn != "xyz"
+ * or groupdn !=
+ */
+ if ( (aci->aci_type & ACI_HAS_DENY_RULE) &&
+ ( (aci->aci_type & ACI_CONTAIN_NOT_USERDN ) ||
+ (aci->aci_type & ACI_CONTAIN_NOT_GROUPDN) ||
+ (aci->aci_type & ACI_CONTAIN_NOT_ROLEDN)) ){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE BECAUSE OF DENY RULE\n", 0,0,0);
+ goto cleanup;
+ }
+
+ /* Must be a anyone rule */
+ if ( aci->aci_elevel != ACI_ELEVEL_USERDN_ANYONE ) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ }
+ if (! (aci->aci_access & ( SLAPI_ACL_READ | SLAPI_ACL_SEARCH)) ) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ }
+ /* If the rule has anything other than userdn = "ldap:///anyone"
+ ** let's not consider complex rules - let's make this lean.
+ */
+ if ( aci->aci_ruleType & ~ACI_USERDN_RULE ){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE BECAUSE OF COMPLEX RULE\n", 0,0,0);
+ goto cleanup;
+ }
+
+ /* Must not be a or have a
+ ** 1 ) DENY RULE 2) targetfilter
+ ** 3) no target pattern ( skip monitor acl )
+ */
+ if ( aci->aci_type & ( ACI_HAS_DENY_RULE | ACI_TARGET_PATTERN |
+ ACI_TARGET_NOT | ACI_TARGET_FILTER_NOT )) {
+ const char *dn = slapi_sdn_get_dn ( aci->aci_sdn );
+
+ /* see if this is a monitor acl */
+ if (( strcasecmp ( dn, "cn=monitor") == 0 ) ||
+ ( strcasecmp ( dn, "cn=monitor,cn=ldbm") == 0 )) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ } else {
+ /* clean up before leaving */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE 1\n", 0,0,0);
+ goto cleanup;
+ }
+
+ }
+
+ /* Now we have an ALLOW ACL which applies to anyone */
+ a_numacl = a_profile->anom_numacls++;
+
+ if ( a_profile->anom_numacls == ACL_ANOM_MAX_ACL ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "CANCELLING ANOM USER PROFILE 2\n", 0,0,0);
+ goto cleanup;
+ }
+
+ if ( (f = aci->target) != NULL ) {
+ char *avaType;
+ struct berval *avaValue;
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ a_profile->anom_targetinfo[a_numacl].anom_target =
+ slapi_sdn_new_dn_byval ( avaValue->bv_val );
+ } else {
+ a_profile->anom_targetinfo[a_numacl].anom_target =
+ slapi_sdn_dup ( aci->aci_sdn );
+ }
+
+ a_profile->anom_targetinfo[a_numacl].anom_filter = NULL;
+ if ( aci->targetFilterStr )
+ a_profile->anom_targetinfo[a_numacl].anom_filter = slapi_str2filter ( aci->targetFilterStr );
+
+ i = 0;
+ srcattrArray = aci->targetAttr;
+ while ( srcattrArray[i])
+ i++;
+
+ a_profile->anom_targetinfo[a_numacl].anom_targetAttrs =
+ (char **) slapi_ch_calloc ( 1, (i+1) * sizeof(char *));
+
+ srcattrArray = aci->targetAttr;
+ destattrArray = a_profile->anom_targetinfo[a_numacl].anom_targetAttrs;
+
+ i = 0;
+ while ( srcattrArray[i] ) {
+ attr = srcattrArray[i];
+ if ( attr->attr_type & ACL_ATTR_FILTER ) {
+ /* Do'nt want to support these kind now */
+ destattrArray[i] = NULL;
+ /* clean up before leaving */
+ __aclanom__del_profile ();
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE 3\n", 0,0,0);
+ goto cleanup;
+ }
+
+ destattrArray[i] = slapi_ch_strdup ( attr->u.attr_str );
+ i++;
+ }
+
+ destattrArray[i] = NULL;
+
+ aclutil_print_aci ( aci, "anom" );
+ /* Here we are storing att the info from the acls. However
+ ** we are only interested in a few things like ACI_TARGETATTR_NOT.
+ */
+ a_profile->anom_targetinfo[a_numacl].anom_type = aci->aci_type;
+ a_profile->anom_targetinfo[a_numacl].anom_access = aci->aci_access;
+
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ }
+
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+ return;
+
+cleanup:
+ __aclanom__del_profile ();
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+}
+
+
+void
+aclanom_invalidateProfile ()
+{
+ ANOM_LOCK_WRITE();
+ if ( acl_anom_profile && acl_anom_profile->anom_numacls )
+ acl_anom_profile->anom_signature = 0;
+ ANOM_UNLOCK_WRITE();
+
+
+}
+
+/*
+ * __aclanom_del_profile
+ *
+ * Cleanup the anonymous user's profile we have.
+ *
+ * ASSUMPTION: A WRITE LOCK HAS BEEN OBTAINED
+ *
+ */
+static void
+__aclanom__del_profile (void)
+{
+ int i;
+ struct anom_profile *a_profile;
+
+
+ if ( (a_profile = acl_anom_profile) == NULL ) {
+ return;
+ }
+
+ for ( i=0; i < a_profile->anom_numacls; i++ ) {
+ int j = 0;
+ char **destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
+
+ /* Deallocate target */
+ slapi_sdn_free ( &a_profile->anom_targetinfo[i].anom_target );
+
+ /* Deallocate filter */
+ if ( a_profile->anom_targetinfo[i].anom_filter )
+ slapi_filter_free ( a_profile->anom_targetinfo[i].anom_filter, 1 );
+
+ /* Deallocate attrs */
+ if ( destArray ) {
+ while ( destArray[j] ) {
+ slapi_ch_free ( (void **) &destArray[j] );
+ j++;
+ }
+ slapi_ch_free ( (void **) &destArray );
+ }
+ a_profile->anom_targetinfo[i].anom_targetAttrs = NULL;
+ a_profile->anom_targetinfo[i].anom_type = 0;
+ a_profile->anom_targetinfo[i].anom_access = 0;
+ }
+ a_profile->anom_numacls = 0;
+
+ /* Don't clean the signatue */
+}
+
+/*
+ * This routine sets up a "context" for evaluation of access control
+ * on a given entry for an anonymous user.
+ * It just factors out the scope and targetfilter info into a list
+ * of indices of the global anom profile list, that apply to this
+ * entry, and stores them in the aclpb.
+ * It's use relies on the way that access control is checked in the mailine search
+ * code in the core server, namely: check filter, check entry, then check each
+ * attribute. So, we call this in acl_access_allowed() before calling
+ * aclanom_match_profile()--therafter, aclanom_match_profile() uses the
+ * context to evaluate access to the entry and attributes.
+ *
+ * If there are no anom profiles, or the anom profiles get cancelled
+ * due to complex anon acis, then that's OK, aclanom_match_profile()
+ * returns -1 and the mainline acl code kicks in.
+ *
+ * The lifetime of this context info is the time it takes to check
+ * access control for all parts of this entry (filter, entry, attributes).
+ * So, if for an example an entry changes and a given anom profile entry
+ * no longer applies, we will not notice until the next round of access
+ * control checking on the entry--this is acceptable.
+ *
+ * The gain on doing this factoring in the following type of search
+ * was approx 6%:
+ * anon bind, 20 threads, exact match, ~20 attributes returned,
+ * (searchrate & DirectoryMark).
+ *
+*/
+void
+aclanom_get_suffix_info(Slapi_Entry *e,
+ struct acl_pblock *aclpb ) {
+ int i;
+ char *ndn = NULL;
+ Slapi_DN *e_sdn;
+ const char *aci_ndn;
+ int populate = 0;
+ struct scoped_entry_anominfo *s_e_anominfo =
+ &aclpb->aclpb_scoped_entry_anominfo;
+
+ ANOM_LOCK_READ ();
+
+ s_e_anominfo->anom_e_nummatched=0;
+
+ ndn = slapi_entry_get_ndn ( e ) ;
+ e_sdn= slapi_entry_get_sdn ( e ) ;
+ for (i=acl_anom_profile->anom_numacls-1; i >= 0; i-- ) {
+ aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
+ if (!slapi_sdn_issuffix(e_sdn,acl_anom_profile->anom_targetinfo[i].anom_target)
+ || (!slapi_is_rootdse(ndn) && slapi_is_rootdse(aci_ndn)))
+ continue;
+ if ( acl_anom_profile->anom_targetinfo[i].anom_filter ) {
+ if ( slapi_vattr_filter_test( aclpb->aclpb_pblock, e,
+ acl_anom_profile->anom_targetinfo[i].anom_filter,
+ 0 /*don't do acess chk*/) != 0)
+ continue;
+ }
+ s_e_anominfo->anom_e_targetInfo[s_e_anominfo->anom_e_nummatched]=i;
+ s_e_anominfo->anom_e_nummatched++;
+ }
+ ANOM_UNLOCK_READ ();
+}
+
+
+/*
+ * aclanom_match_profile
+ * Look at the anonymous profile and see if we can use it or not.
+ *
+ *
+ * Inputs:
+ * Slapi_Pblock - The Pblock
+ * Slapi_Entry *e - The entry for which we are asking permission.
+ * char *attr - Attribute name
+ * int access - access type
+ *
+ * Return:
+ * LDAP_SUCCESS ( 0 ) - acess is allowed.
+ * LDAP_INSUFFICIENT_ACCESS (50 ) - access denied.
+ * -1 - didn't match the targets
+ *
+ * Assumptions:
+ * The caller of this module has to make sure that the client is
+ * an anonymous client.
+ */
+int
+aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb, Slapi_Entry *e,
+ char *attr, int access )
+{
+
+ struct anom_profile *a_profile;
+ int result, i, k;
+ char **destArray;
+ int tmatched = 0;
+ char ebuf[ BUFSIZ ];
+ int loglevel;
+ struct scoped_entry_anominfo *s_e_anominfo =
+ &aclpb->aclpb_scoped_entry_anominfo;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ /* WE are only interested for READ/SEARCH */
+ if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) )
+ return -1;
+
+ /* If we are here means, the client is doing a anonymous read/search */
+ if ((a_profile = acl_anom_profile) == NULL ) {
+ return -1;
+ }
+
+ ANOM_LOCK_READ ();
+ /* Check the signature first */
+ if ( a_profile->anom_signature != acl_get_aclsignature () ) {
+ /* Need to regenrate the signature.
+ * Need a WRITE lock to generate the anom profile -
+ * which is obtained in acl__gen_anom_user_profile (). Since
+ * I don't have upgrade lock -- I have to do this way.
+ */
+ ANOM_UNLOCK_READ ();
+ aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
+ aclanom_get_suffix_info(e, aclpb );
+ ANOM_LOCK_READ ();
+ }
+
+ /* doing this early saves use a malloc/free/normalize cost */
+ if ( !a_profile->anom_numacls ) {
+ ANOM_UNLOCK_READ ();
+ return -1;
+ }
+
+ result = LDAP_INSUFFICIENT_ACCESS;
+
+ for ( k=0; k<s_e_anominfo->anom_e_nummatched; k++ ) {
+ short matched = 0;
+ short j = 0;
+
+ i = s_e_anominfo->anom_e_targetInfo[k];
+
+ /* Check for right */
+ if ( !(a_profile->anom_targetinfo[i].anom_access & access) )
+ continue;
+
+ /*
+ * XXX rbyrne Don't really understand the role of this
+ * but not causing any obvious bugs...get back to it.
+ */
+ tmatched++;
+
+ if ( attr == NULL ) {
+ result = LDAP_SUCCESS;
+ break;
+ }
+
+ destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
+ while ( destArray[j] ) {
+ if ( strcasecmp ( destArray[j], "*") == 0 ||
+ slapi_attr_type_cmp ( attr, destArray[j], 1 ) == 0 ) {
+ matched = 1;
+ break;
+ }
+ j++;
+ }
+
+ if ( a_profile->anom_targetinfo[i].anom_type & ACI_TARGET_ATTR_NOT )
+ result = matched ? LDAP_INSUFFICIENT_ACCESS : LDAP_SUCCESS;
+ else
+ result = matched ? LDAP_SUCCESS : LDAP_INSUFFICIENT_ACCESS;
+
+ if ( result == LDAP_SUCCESS )
+ break;
+ } /* for */
+
+ if ( slapi_is_loglevel_set(loglevel) ) {
+ char *ndn = NULL;
+ Slapi_Operation *op = NULL;
+
+ ndn = slapi_entry_get_ndn ( e ) ;
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ if ( result == LDAP_SUCCESS) {
+ const char *aci_ndn;
+ aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
+
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d: Allow access on entry(%s).attr(%s) to anonymous: acidn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ escape_string_with_punctuation(ndn, ebuf),
+ attr ? attr:"NULL",
+ escape_string_with_punctuation(aci_ndn, ebuf));
+ } else {
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d: Deny access on entry(%s).attr(%s) to anonymous\n",
+ op->o_connid, op->o_opid,
+ escape_string_with_punctuation(ndn, ebuf), attr ? attr:"NULL" );
+ }
+ }
+
+ ANOM_UNLOCK_READ ();
+ if ( tmatched == 0)
+ return -1;
+ else
+ return result;
+
+}
+int
+aclanom_is_client_anonymous ( Slapi_PBlock *pb )
+{
+ char *clientDn;
+
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+ if (acl_anom_profile->anom_numacls &&
+ acl_anom_profile->anom_signature &&
+ (( NULL == clientDn) || (clientDn && *clientDn == '\0')) )
+ return 1;
+
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/acl/acldllmain.c b/ldap/servers/plugins/acl/acldllmain.c
new file mode 100644
index 00000000..21ab60c4
--- /dev/null
+++ b/ldap/servers/plugins/acl/acldllmain.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/acl/acleffectiverights.c b/ldap/servers/plugins/acl/acleffectiverights.c
new file mode 100644
index 00000000..1e250e96
--- /dev/null
+++ b/ldap/servers/plugins/acl/acleffectiverights.c
@@ -0,0 +1,674 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2004 Netscape Communications Corporation
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "acl.h"
+
+static int
+_ger_g_permission_granted ( Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf )
+{
+ char *proxydn = NULL;
+ Slapi_DN *requestor_sdn, *entry_sdn;
+ char *errtext = NULL;
+ int isroot;
+ int rc;
+
+ /*
+ * Theorically, we should check if the entry has "g"
+ * permission granted to the requestor. If granted,
+ * allows the effective rights on that entry and its
+ * attributes within the entry to be returned for
+ * ANY subject.
+ *
+ * "G" permission granting has not been implemented yet,
+ * the current release assumes that "g" permission be
+ * granted to root and owner of any entry.
+ */
+
+ /*
+ * The requestor may be either the bind dn or a proxy dn
+ */
+ acl_get_proxyauth_dn ( pb, &proxydn, &errtext );
+ if ( proxydn != NULL )
+ {
+ requestor_sdn = slapi_sdn_new_dn_passin ( proxydn );
+ }
+ else
+ {
+ requestor_sdn = &(pb->pb_op->o_sdn);
+ }
+ if ( slapi_sdn_get_dn (requestor_sdn) == NULL )
+ {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_g_permission_granted: anonymous has no g permission\n" );
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto bailout;
+ }
+ isroot = slapi_dn_isroot ( slapi_sdn_get_dn (requestor_sdn) );
+ if ( isroot )
+ {
+ /* Root has "g" permission on any entry */
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ entry_sdn = slapi_entry_get_sdn ( e );
+ if ( entry_sdn == NULL || slapi_sdn_get_dn (entry_sdn) == NULL )
+ {
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ if ( slapi_sdn_compare ( requestor_sdn, entry_sdn ) == 0 )
+ {
+ /* Owner has "g" permission on his own entry */
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ aclutil_str_appened ( errbuf, "get-effective-rights: requestor has no g permission on the entry" );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_g_permission_granted: %s\n", *errbuf);
+ rc = LDAP_INSUFFICIENT_ACCESS;
+
+bailout:
+ if ( proxydn )
+ {
+ /* The ownership of proxydn has passed to requestor_sdn */
+ slapi_sdn_free ( &requestor_sdn );
+ }
+ return rc;
+}
+
+static int
+_ger_parse_control ( Slapi_PBlock *pb, char **subjectndn, int *iscritical, char **errbuf )
+{
+ LDAPControl **requestcontrols;
+ struct berval *subjectber;
+ BerElement *ber;
+
+ if (NULL == subjectndn)
+ {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ *subjectndn = NULL;
+
+ /*
+ * Get the control
+ */
+ slapi_pblock_get ( pb, SLAPI_REQCONTROLS, (void *) &requestcontrols );
+ slapi_control_present ( requestcontrols,
+ LDAP_CONTROL_GET_EFFECTIVE_RIGHTS,
+ &subjectber,
+ iscritical );
+ if ( subjectber == NULL || subjectber->bv_val == NULL ||
+ subjectber->bv_len == 0 )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: missing subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( strncasecmp ( "dn:", subjectber->bv_val, 3 ) == 0 )
+ {
+ /*
+ * This is a non-standard support to allow the subject being a plain
+ * or base64 encoding string. Hence users using -J option in
+ * ldapsearch don't have to do BER encoding for the subject.
+ */
+ *subjectndn = slapi_ch_malloc ( subjectber->bv_len + 1 );
+ strncpy ( *subjectndn, subjectber->bv_val, subjectber->bv_len );
+ *(*subjectndn + subjectber->bv_len) = '\0';
+ }
+ else
+ {
+ ber = ber_init (subjectber);
+ if ( ber == NULL )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: ber_init failed for the subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_OPERATIONS_ERROR;
+ }
+ /* "a" means to allocate storage as needed for octet string */
+ if ( ber_scanf (ber, "a", subjectndn) == LBER_ERROR )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: invalid ber tag in the subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ ber_free ( ber, 1 );
+ return LDAP_INVALID_SYNTAX;
+ }
+ ber_free ( ber, 1 );
+ }
+
+ /*
+ * The current implementation limits the subject to authorization ID
+ * (see section 9 of RFC 2829) only. It also only supports the "dnAuthzId"
+ * flavor, which looks like "dn:<DN>" where null <DN> is for anonymous.
+ */
+ if ( NULL == *subjectndn || strlen (*subjectndn) < 3 ||
+ strncasecmp ( "dn:", *subjectndn, 3 ) != 0 )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: subject is not dnAuthzId" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ strcpy ( *subjectndn, *subjectndn + 3 );
+ slapi_dn_normalize ( *subjectndn );
+ return LDAP_SUCCESS;
+}
+
+static void
+_ger_release_gerpb (
+ Slapi_PBlock **gerpb,
+ void **aclcb, /* original aclcb */
+ Slapi_PBlock *pb /* original pb */
+ )
+{
+ if ( *gerpb )
+ {
+ /* Return conn to pb */
+ slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, NULL );
+ slapi_pblock_destroy ( *gerpb );
+ *gerpb = NULL;
+ }
+
+ /* Put the original aclcb back to pb */
+ if ( *aclcb )
+ {
+ Connection *conn = NULL;
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn );
+ if (conn)
+ {
+ struct aclcb *geraclcb;
+ geraclcb = (struct aclcb *) acl_get_ext ( ACL_EXT_CONNECTION, conn );
+ acl_conn_ext_destructor ( geraclcb, NULL, NULL );
+ acl_set_ext ( ACL_EXT_CONNECTION, conn, *aclcb );
+ *aclcb = NULL;
+ }
+ }
+}
+
+static int
+_ger_new_gerpb (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ Slapi_PBlock **gerpb,
+ void **aclcb, /* original aclcb */
+ char **errbuf
+ )
+{
+ Connection *conn;
+ struct acl_cblock *geraclcb;
+ Acl_PBlock *aclpb, *geraclpb;
+ Operation *op, *gerop;
+ int rc = LDAP_SUCCESS;
+
+ *aclcb = NULL;
+ *gerpb = slapi_pblock_new ();
+ if ( *gerpb == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+
+ {
+ /* aclpb initialization needs the backend */
+ Slapi_Backend *be;
+ slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_set ( *gerpb, SLAPI_BACKEND, be );
+ }
+
+ {
+ int isroot = slapi_dn_isroot ( subjectndn );
+ slapi_pblock_set ( *gerpb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ }
+
+ /* Save requestor's aclcb and set subjectdn's one */
+ {
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn );
+ slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, conn );
+
+ /* Can't share the conn->aclcb because of different context */
+ geraclcb = (struct acl_cblock *) acl_conn_ext_constructor ( NULL, NULL);
+ if ( geraclcb == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+ slapi_sdn_set_ndn_byval ( geraclcb->aclcb_sdn, subjectndn );
+ *aclcb = acl_get_ext ( ACL_EXT_CONNECTION, conn );
+ acl_set_ext ( ACL_EXT_CONNECTION, conn, (void *) geraclcb );
+ }
+
+ {
+ gerop = operation_new ( OP_FLAG_INTERNAL );
+ if ( gerop == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+ /*
+ * conn is a no-use parameter in the functions
+ * chained down from factory_create_extension
+ */
+ gerop->o_extension = factory_create_extension ( get_operation_object_type(), (void *)gerop, (void *)conn );
+ slapi_pblock_set ( *gerpb, SLAPI_OPERATION, gerop );
+ slapi_sdn_set_dn_byval ( &gerop->o_sdn, subjectndn );
+ geraclpb = acl_get_ext ( ACL_EXT_OPERATION, (void *)gerop);
+ acl_init_aclpb ( *gerpb, geraclpb, subjectndn, 0 );
+ geraclpb->aclpb_res_type |= ACLPB_EFFECTIVE_RIGHTS;
+ }
+
+
+bailout:
+ if ( rc != LDAP_SUCCESS )
+ {
+ _ger_release_gerpb ( gerpb, aclcb, pb );
+ }
+
+ return rc;
+}
+
+/*
+ * Callers should have already allocated *gerstr to hold at least
+ * "entryLevelRights: adnvxxx\n".
+ */
+unsigned long
+_ger_get_entry_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char *gerstr,
+ char **errbuf
+ )
+{
+ unsigned long entryrights = 0;
+ Slapi_RDN *rdn = NULL;
+ const char *rdnstr = NULL;
+ char *equalsign = NULL;
+ char *rdntype = NULL;
+
+ strcpy ( gerstr, "entryLevelRights: " );
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_READ\n" );
+ if (acl_access_allowed(gerpb, e, "*", NULL, SLAPI_ACL_READ) == LDAP_SUCCESS)
+ {
+ /* v - view e */
+ entryrights |= SLAPI_ACL_READ;
+ strcat (gerstr, "v");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_ADD\n" );
+ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_ADD) == LDAP_SUCCESS)
+ {
+ /* a - add child entry below e */
+ entryrights |= SLAPI_ACL_ADD;
+ strcat (gerstr, "a");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_DELETE\n" );
+ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_DELETE) == LDAP_SUCCESS)
+ {
+ /* d - delete e */
+ entryrights |= SLAPI_ACL_DELETE;
+ strcat (gerstr, "d");
+ }
+ /*
+ * Some limitation/simplification applied here:
+ * - The modrdn right requires the rights to delete the old rdn and
+ * the new one. However we have no knowledge of what the new rdn
+ * is going to be.
+ * - In multi-valued RDN case, we check the right on
+ * the first rdn type only for now.
+ */
+ rdn = slapi_rdn_new_dn ( slapi_entry_get_ndn (e) );
+ rdnstr = slapi_rdn_get_rdn ( rdn );
+ if ( NULL != (equalsign = strchr ( rdnstr, '=' )) )
+ {
+ rdntype = slapi_ch_malloc ( equalsign-rdnstr+1 );
+ strncpy ( rdntype, rdnstr, equalsign-rdnstr );
+ rdntype [ equalsign-rdnstr ] = '\0';
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype );
+ if (acl_access_allowed(gerpb, e, rdntype, NULL,
+ ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS &&
+ acl_access_allowed(gerpb, e, rdntype, NULL,
+ ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* n - rename e */
+ entryrights |= SLAPI_ACL_WRITE;
+ strcat (gerstr, "n");
+ }
+ slapi_ch_free ( (void**) &rdntype );
+ }
+ slapi_rdn_free ( &rdn );
+
+done:
+ if ( entryrights == 0 )
+ {
+ strcat (gerstr, "none");
+ }
+
+ strcat (gerstr, "\n");
+
+ return entryrights;
+}
+
+/*
+ * *gerstr should point to a heap buffer since it may need
+ * to expand dynamically.
+ */
+unsigned long
+_ger_get_attr_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char *type,
+ char **gerstr,
+ int *gerstrsize,
+ int isfirstattr,
+ char **errbuf
+ )
+{
+ unsigned long attrrights = 0;
+
+ /* Enough space for " $type:rwoscxx" ? */
+ if ( (*gerstrsize - strlen(*gerstr)) < (strlen(type) + 16) )
+ {
+ /* slapi_ch_realloc() exits if realloc() failed */
+ *gerstrsize += 256;
+ *gerstr = slapi_ch_realloc ( *gerstr, *gerstrsize );
+ }
+ if (!isfirstattr)
+ {
+ strcat ( *gerstr, ", " );
+ }
+ sprintf ( *gerstr + strlen(*gerstr), "%s:", type );
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_READ %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_READ) == LDAP_SUCCESS)
+ {
+ /* r - read the values of type */
+ attrrights |= SLAPI_ACL_READ;
+ strcat (*gerstr, "r");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_SEARCH %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_SEARCH) == LDAP_SUCCESS)
+ {
+ /* s - search the values of type */
+ attrrights |= SLAPI_ACL_SEARCH;
+ strcat (*gerstr, "s");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_COMPARE %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_COMPARE) == LDAP_SUCCESS)
+ {
+ /* c - compare the values of type */
+ attrrights |= SLAPI_ACL_COMPARE;
+ strcat (*gerstr, "c");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_WRITE_ADD %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* w - add the values of type */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD;
+ strcat (*gerstr, "w");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_WRITE_DEL %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS)
+ {
+ /* o - delete the values of type */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL;
+ strcat (*gerstr, "o");
+ }
+ /* If subjectdn has no general write right, check for self write */
+ if ( 0 == (attrrights & (ACLPB_SLAPI_ACL_WRITE_DEL | ACLPB_SLAPI_ACL_WRITE_ADD)) )
+ {
+ struct berval val;
+
+ val.bv_val = (char *)subjectndn;
+ val.bv_len = strlen (subjectndn);
+
+ if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* W - add self to the attribute */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD;
+ strcat (*gerstr, "W");
+ }
+ if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS)
+ {
+ /* O - delete self from the attribute */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL;
+ strcat (*gerstr, "O");
+ }
+ }
+
+ if ( attrrights == 0 )
+ {
+ strcat (*gerstr, "none");
+ }
+
+ return attrrights;
+}
+
+void
+_ger_get_attrs_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char **attrs,
+ char **gerstr,
+ int *gerstrsize,
+ char **errbuf
+ )
+{
+ int isfirstattr = 1;
+
+ /* gerstr was initially allocated with enough space for one more line */
+ strcat ( *gerstr, "attributeLevelRights: " );
+
+ if (attrs && *attrs)
+ {
+ int i;
+ for ( i = 0; attrs[i]; i++ )
+ {
+ _ger_get_attr_rights ( gerpb, e, subjectndn, attrs[i], gerstr, gerstrsize, isfirstattr, errbuf );
+ isfirstattr = 0;
+ }
+ }
+ else
+ {
+ Slapi_Attr *prevattr = NULL, *attr;
+ char *type;
+
+ while ( slapi_entry_next_attr ( e, prevattr, &attr ) == 0 )
+ {
+ if ( ! slapi_attr_flag_is_set (attr, SLAPI_ATTR_FLAG_OPATTR) )
+ {
+ slapi_attr_get_type ( attr, &type );
+ _ger_get_attr_rights ( gerpb, e, subjectndn, type, gerstr, gerstrsize, isfirstattr, errbuf );
+ isfirstattr = 0;
+ }
+ prevattr = attr;
+ }
+ }
+
+ if ( isfirstattr )
+ {
+ /* not a single attribute was retrived or specified */
+ strcat ( *gerstr, "*:none" );
+ }
+ return;
+}
+
+/*
+ * controlType = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS;
+ * criticality = n/a;
+ * controlValue = OCTET STRING of BER encoding of the SEQUENCE of
+ * ENUMERATED LDAP code
+ */
+void
+_ger_set_response_control (
+ Slapi_PBlock *pb,
+ int iscritical,
+ int rc
+ )
+{
+ LDAPControl **resultctrls = NULL;
+ LDAPControl gerrespctrl;
+ BerElement *ber = NULL;
+ struct berval *berval = NULL;
+ int found = 0;
+ int i;
+
+ if ( (ber = der_alloc ()) == NULL )
+ {
+ goto bailout;
+ }
+
+ /* begin sequence, enumeration, end sequence */
+ ber_printf ( ber, "{e}", rc );
+ if ( ber_flatten ( ber, &berval ) != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+ gerrespctrl.ldctl_oid = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS;
+ gerrespctrl.ldctl_iscritical = iscritical;
+ gerrespctrl.ldctl_value.bv_val = berval->bv_val;
+ gerrespctrl.ldctl_value.bv_len = berval->bv_len;
+
+ slapi_pblock_get ( pb, SLAPI_RESCONTROLS, &resultctrls );
+ for (i = 0; resultctrls && resultctrls[i]; i++)
+ {
+ if (strcmp(resultctrls[i]->ldctl_oid, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS) == 0)
+ {
+ /*
+ * We get here if search returns more than one entry
+ * and this is not the first entry.
+ */
+ ldap_control_free ( resultctrls[i] );
+ resultctrls[i] = slapi_dup_control (&gerrespctrl);
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ /* slapi_pblock_set() will dup the control */
+ slapi_pblock_set ( pb, SLAPI_ADD_RESCONTROL, &gerrespctrl );
+ }
+
+bailout:
+ ber_free ( ber, 1 ); /* ber_free() checks for NULL param */
+ ber_bvfree ( berval ); /* ber_bvfree() checks for NULL param */
+}
+
+int
+acl_get_effective_rights (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* target entry */
+ char **attrs, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access, /* requested access rights */
+ char **errbuf
+ )
+{
+ Slapi_PBlock *gerpb = NULL;
+ void *aclcb = NULL;
+ char *subjectndn = NULL;
+ char *gerstr = NULL;
+ int gerstrsize = 1024;
+ unsigned long entryrights;
+ int iscritical = 1;
+ int rc;
+
+ *errbuf = '\0';
+ gerstr = slapi_ch_malloc ( gerstrsize );
+
+ /*
+ * Get the subject
+ */
+ rc = _ger_parse_control (pb, &subjectndn, &iscritical, errbuf );
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /*
+ * The requestor should have g permission on the entry
+ * to get the effective rights.
+ */
+ rc = _ger_g_permission_granted (pb, e, errbuf);
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /*
+ * Construct a new pb
+ */
+ rc = _ger_new_gerpb ( pb, e, subjectndn, &gerpb, &aclcb, errbuf );
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /* Get entry level effective rights */
+ entryrights = _ger_get_entry_rights ( gerpb, e, subjectndn, gerstr, errbuf );
+
+ /*
+ * Attribute level effective rights may not be NULL
+ * even if entry level's is.
+ */
+ _ger_get_attrs_rights ( gerpb, e, subjectndn, attrs, &gerstr, &gerstrsize, errbuf );
+
+bailout:
+ /*
+ * Now construct the response control
+ */
+ _ger_set_response_control ( pb, iscritical, rc );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ sprintf ( gerstr, "entryLevelRights: %d\nattributeLevelRights: *:%d", rc, rc );
+ }
+
+ slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name,
+ "###### Effective Rights on Entry (%s) for Subject (%s) ######\n",
+ slapi_entry_get_ndn (e), subjectndn);
+ slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name, "%s\n", gerstr);
+
+ /* Restore pb */
+ _ger_release_gerpb ( &gerpb, &aclcb, pb );
+
+ /*
+ * General plugin uses SLAPI_RESULT_TEXT for error text. Here
+ * SLAPI_PB_RESULT_TEXT is exclusively shared with add, dse and schema.
+ * slapi_pblock_set() will free any previous data, and
+ * pblock_done() will free SLAPI_PB_RESULT_TEXT.
+ */
+ slapi_pblock_set (pb, SLAPI_PB_RESULT_TEXT, gerstr);
+
+ if ( !iscritical )
+ {
+ /*
+ * If return code is not LDAP_SUCCESS, the server would
+ * abort sending the data of the entry to the client.
+ */
+ rc = LDAP_SUCCESS;
+ }
+
+ slapi_ch_free ( (void **) &subjectndn );
+ slapi_ch_free ( (void **) &gerstr );
+ return rc;
+}
diff --git a/ldap/servers/plugins/acl/aclgroup.c b/ldap/servers/plugins/acl/aclgroup.c
new file mode 100644
index 00000000..4cd7039f
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclgroup.c
@@ -0,0 +1,442 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/***************************************************************************
+ *
+ * This module deals with the global user group cache.
+ *
+ * A LRU queue mechanism is used to maintain the groups the user currently in.
+ * At this moment the QUEUE is invalidated if there is a group change. A better
+ * way would have been to invalidate only the one which are effected.
+ * However to accomplish that will require quite a bit of work which may not be
+ * cost-efftive.
+ **************************************************************************/
+static aclGroupCache *aclUserGroups;
+#define ACL_MAXCACHE_USERGROUPS 200
+
+#define ACLG_LOCK_GROUPCACHE_READ() PR_RWLock_Rlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_LOCK_GROUPCACHE_WRITE() PR_RWLock_Wlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_ULOCK_GROUPCACHE_WRITE() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_ULOCK_GROUPCACHE_READ() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
+
+
+static void __aclg__delete_userGroup ( aclUserGroup *u_group );
+
+
+int
+aclgroup_init ()
+{
+
+ aclUserGroups = ( aclGroupCache * ) slapi_ch_calloc (1, sizeof ( aclGroupCache ) );
+ if ( NULL == (aclUserGroups->aclg_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"Group LOCK"))) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to allocate RWLOCK for group cache\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * aclg_init_userGroup
+ *
+ * Go thru the Global Group CACHE and see if we have group information for
+ * the user. The user's group cache is invalidated when a group is modified
+ * (in which case ALL usergroups are invalidated) or when the user's entry
+ * is modified in which case just his is invalidated.
+ *
+ * We need to scan the whole cache looking for a valid entry that matches
+ * this user. If we find invalid entries along the way.
+ *
+ * If we don't have anything it's fine. we will allocate a space when we
+ * need it i.e during the group evaluation.
+ *
+ * Inputs:
+ * struct acl_pblock - ACL private block
+ * char *dn - the client's dn
+ * int got_lock - 1: already obtained WRITE Lock
+ * - 0: Nope; get one
+ * Returns:
+ * None.
+ */
+
+void
+aclg_init_userGroup ( struct acl_pblock *aclpb, const char *n_dn , int got_lock )
+{
+ aclUserGroup *u_group = NULL;
+ aclUserGroup *next_ugroup = NULL;
+ aclUserGroup *p_group, *n_group;
+ int found = 0;
+
+ /* Check for Anonymous user */
+ if ( n_dn && *n_dn == '\0') return;
+
+ if ( !got_lock ) ACLG_LOCK_GROUPCACHE_WRITE ();
+ u_group = aclUserGroups->aclg_first;
+ aclpb->aclpb_groupinfo = NULL;
+
+ while ( u_group != NULL ) {
+ next_ugroup = u_group->aclug_next;
+ if ( aclUserGroups->aclg_signature != u_group->aclug_signature) {
+ /*
+ * This means that this usergroup is no longer valid and
+ * this operation so delete this one if no one is using it.
+ */
+
+ if ( !u_group->aclug_refcnt ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "In traversal group deallocation\n", 0,0,0 );
+ __aclg__delete_userGroup (u_group);
+ }
+ } else {
+
+ /*
+ * Here, u_group is valid--if it matches then take it.
+ */
+ if ( slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
+ (ACLUCHP)n_dn ) == 0 ) {
+ u_group->aclug_refcnt++;
+ aclpb->aclpb_groupinfo = u_group;
+ found = 1;
+ break;
+ }
+ }
+ u_group = next_ugroup;
+ }
+
+ /* Move the new one to the top of the queue */
+ if ( found ) {
+ p_group = u_group->aclug_prev;
+ n_group = u_group->aclug_next;
+
+ if ( p_group ) {
+ aclUserGroup *t_group = NULL;
+
+ p_group->aclug_next = n_group;
+ if ( n_group ) n_group->aclug_prev = p_group;
+
+ t_group = aclUserGroups->aclg_first;
+ if ( t_group ) t_group->aclug_prev = u_group;
+
+ u_group->aclug_prev = NULL;
+ u_group->aclug_next = t_group;
+ aclUserGroups->aclg_first = u_group;
+
+ if ( u_group == aclUserGroups->aclg_last )
+ aclUserGroups->aclg_last = p_group;
+ }
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "acl_init_userGroup: found in cache for dn:%s\n", n_dn,0,0);
+ }
+ if (!got_lock ) ACLG_ULOCK_GROUPCACHE_WRITE ();
+}
+
+
+/*
+ *
+ * aclg_reset_userGroup
+ * Reset the reference count to the user's group.
+ *
+ * Inputs:
+ * struct acl_pblock -- The acl private block.
+ * Returns:
+ * None.
+ *
+ * Note: A WRITE Lock on the GroupCache is obtained during the change:
+ */
+void
+aclg_reset_userGroup ( struct acl_pblock *aclpb )
+{
+
+ aclUserGroup *u_group;
+
+ ACLG_LOCK_GROUPCACHE_WRITE();
+
+ if ( (u_group = aclpb->aclpb_groupinfo) != NULL ) {
+ u_group->aclug_refcnt--;
+
+ /* If I am the last one but I was using an invalid group cache
+ ** in the meantime, it is time now to get rid of it so that we will
+ ** not have duplicate cache.
+ */
+ if ( !u_group->aclug_refcnt &&
+ ( aclUserGroups->aclg_signature != u_group->aclug_signature )) {
+ __aclg__delete_userGroup ( u_group );
+ }
+ }
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ aclpb->aclpb_groupinfo = NULL;
+}
+
+/*
+ * Find a user group in the global cache, returning a pointer to it,
+ * ensuring that the refcnt has been bumped to stop
+ * another thread freeing it underneath us.
+*/
+
+aclUserGroup*
+aclg_find_userGroup(char *n_dn)
+{
+ aclUserGroup *u_group = NULL;
+ int i;
+
+ /* Check for Anonymous user */
+ if ( n_dn && *n_dn == '\0') return (NULL) ;
+
+ ACLG_LOCK_GROUPCACHE_READ ();
+ u_group = aclUserGroups->aclg_first;
+
+ for ( i=0; i < aclUserGroups->aclg_num_userGroups; i++ ) {
+ if ( aclUserGroups->aclg_signature == u_group->aclug_signature &&
+ slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
+ (ACLUCHP)n_dn ) == 0 ) {
+ aclg_reader_incr_ugroup_refcnt(u_group);
+ break;
+ }
+ u_group = u_group->aclug_next;
+ }
+
+ ACLG_ULOCK_GROUPCACHE_READ ();
+ return(u_group);
+}
+
+/*
+ * Mark a usergroup for removal from the usergroup cache.
+ * It will be removed by the first operation traversing the cache
+ * that finds it.
+*/
+void
+aclg_markUgroupForRemoval ( aclUserGroup* u_group) {
+
+ ACLG_LOCK_GROUPCACHE_WRITE ();
+ aclg_regen_ugroup_signature(u_group);
+ u_group->aclug_refcnt--;
+ ACLG_ULOCK_GROUPCACHE_WRITE ();
+}
+
+/*
+ *
+ * aclg_get_usersGroup
+ *
+ * If we already have a the group info then we are done. If we
+ * don't, then allocate a new one and attach it.
+ *
+ * Inputs:
+ * struct acl_pblock -- The acl private block.
+ * char *n_dn - normalized client's DN
+ *
+ * Returns:
+ * aclUserGroup - The Group info block.
+ *
+ */
+aclUserGroup *
+aclg_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn)
+{
+
+ aclUserGroup *u_group, *f_group;
+
+ if ( aclpb && aclpb->aclpb_groupinfo )
+ return aclpb->aclpb_groupinfo;
+
+ ACLG_LOCK_GROUPCACHE_WRITE();
+
+ /* try it one more time. We might have one in the meantime */
+ aclg_init_userGroup (aclpb, n_dn , 1 /* got the lock */);
+ if ( aclpb && aclpb->aclpb_groupinfo ) {
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ return aclpb->aclpb_groupinfo;
+ }
+
+ /*
+ * It is possible at this point that we already have a group cache for the user
+ * but is is invalid. We can't use it anayway. So, we march along and allocate a new one.
+ * That's fine as the invalid one will be deallocated when done.
+ */
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "ALLOCATING GROUP FOR:%s\n", n_dn,0,0 );
+ u_group = ( aclUserGroup * ) slapi_ch_calloc ( 1, sizeof ( aclUserGroup ) );
+
+ u_group->aclug_refcnt = 1;
+ if ( (u_group->aclug_refcnt_mutex = PR_NewLock()) == NULL ) {
+ slapi_ch_free((void **)&u_group);
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ return(NULL);
+ }
+
+ u_group->aclug_member_groups = (char **)
+ slapi_ch_calloc ( 1,
+ (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
+ u_group->aclug_member_group_size = ACLUG_INCR_GROUPS_LIST;
+ u_group->aclug_numof_member_group = 0;
+
+ u_group->aclug_notmember_groups = (char **)
+ slapi_ch_calloc ( 1,
+ (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
+ u_group->aclug_notmember_group_size = ACLUG_INCR_GROUPS_LIST;
+ u_group->aclug_numof_notmember_group = 0;
+
+ u_group->aclug_ndn = slapi_ch_strdup ( n_dn ) ;
+
+ u_group->aclug_signature = aclUserGroups->aclg_signature;
+
+ /* Do we have alreday the max number. If we have then delete the last one */
+ if ( aclUserGroups->aclg_num_userGroups >= ACL_MAXCACHE_USERGROUPS - 5 ) {
+ aclUserGroup *d_group;
+
+ /* We need to traverse thru backwards and delete the one with a refcnt = 0 */
+ d_group = aclUserGroups->aclg_last;
+ while ( d_group ) {
+ if ( !d_group->aclug_refcnt ) {
+ __aclg__delete_userGroup ( d_group );
+ break;
+ } else {
+ d_group = d_group->aclug_prev;
+ }
+ }
+
+ /* If we didn't find any, which should be never,
+ ** we have 5 more tries to do it.
+ */
+ }
+ f_group = aclUserGroups->aclg_first;
+ u_group->aclug_next = f_group;
+ if ( f_group ) f_group->aclug_prev = u_group;
+
+ aclUserGroups->aclg_first = u_group;
+ if ( aclUserGroups->aclg_last == NULL )
+ aclUserGroups->aclg_last = u_group;
+
+ aclUserGroups->aclg_num_userGroups++;
+
+ /* Put it in the queue */
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+
+ /* Now hang on to it */
+ aclpb->aclpb_groupinfo = u_group;
+ return u_group;
+}
+
+/*
+ *
+ * __aclg__delete_userGroup
+ *
+ * Delete the User's Group cache.
+ *
+ * Inputs:
+ * aclUserGroup - remove this one
+ * Returns:
+ * None.
+ *
+ * Note: A WRITE Lock on the GroupCache is obtained by the caller
+ */
+static void
+__aclg__delete_userGroup ( aclUserGroup *u_group )
+{
+
+ aclUserGroup *next_group, *prev_group;
+ int i;
+
+ if ( !u_group ) return;
+
+ prev_group = u_group->aclug_prev;
+ next_group = u_group->aclug_next;
+
+ /*
+ * At this point we must have a 0 refcnt or else we are in a bad shape.
+ * If we don't have one then at least remove the user's dn so that it will
+ * be in a condemned state and later deleted.
+ */
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DEALLOCATING GROUP FOR:%s\n", u_group->aclug_ndn,0,0 );
+
+ slapi_ch_free ( (void **) &u_group->aclug_ndn );
+
+ PR_DestroyLock(u_group->aclug_refcnt_mutex);
+
+ /* Remove the member GROUPS */
+ for (i=0; i < u_group->aclug_numof_member_group; i++ )
+ slapi_ch_free ( (void **) &u_group->aclug_member_groups[i] );
+ slapi_ch_free ( (void **) &u_group->aclug_member_groups );
+
+ /* Remove the NOT member GROUPS */
+ for (i=0; i < u_group->aclug_numof_notmember_group; i++ )
+ slapi_ch_free ( (void **) &u_group->aclug_notmember_groups[i] );
+ slapi_ch_free ( (void **) &u_group->aclug_notmember_groups );
+
+ slapi_ch_free ( (void **) &u_group );
+
+ if ( prev_group == NULL && next_group == NULL ) {
+ aclUserGroups->aclg_first = NULL;
+ aclUserGroups->aclg_last = NULL;
+ } else if ( prev_group == NULL ) {
+ next_group->aclug_prev = NULL;
+ aclUserGroups->aclg_first = next_group;
+ } else {
+ prev_group->aclug_next = next_group;
+ if ( next_group )
+ next_group->aclug_prev = prev_group;
+ else
+ aclUserGroups->aclg_last = prev_group;
+ }
+ aclUserGroups->aclg_num_userGroups--;
+}
+
+void
+aclg_regen_group_signature( )
+{
+ aclUserGroups->aclg_signature = aclutil_gen_signature ( aclUserGroups->aclg_signature );
+}
+
+void
+aclg_regen_ugroup_signature( aclUserGroup *ugroup)
+{
+ ugroup->aclug_signature =
+ aclutil_gen_signature ( ugroup->aclug_signature );
+}
+
+void
+aclg_lock_groupCache ( int type /* 1 for reader and 2 for writer */)
+{
+
+ if (type == 1 )
+ ACLG_LOCK_GROUPCACHE_READ();
+ else
+ ACLG_LOCK_GROUPCACHE_WRITE();
+}
+
+void
+aclg_unlock_groupCache ( int type /* 1 for reader and 2 for writer */)
+{
+
+ if (type == 1 )
+ ACLG_ULOCK_GROUPCACHE_READ();
+ else
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+}
+
+
+/*
+ * If you have the write lock on the group cache, you can
+ * increment the refcnt without taking the mutex.
+ * If you just have the reader lock on the refcnt then you need to
+ * take the mutex on the refcnt to increment it--which is what this routine is
+ * for.
+ *
+*/
+
+void
+aclg_reader_incr_ugroup_refcnt(aclUserGroup* u_group) {
+
+ PR_Lock(u_group->aclug_refcnt_mutex);
+ u_group->aclug_refcnt++;
+ PR_Unlock(u_group->aclug_refcnt_mutex);
+}
+
+/* You need the usergroups read lock to call this routine*/
+int
+aclg_numof_usergroups(void) {
+
+ return(aclUserGroups->aclg_num_userGroups);
+}
+
diff --git a/ldap/servers/plugins/acl/aclinit.c b/ldap/servers/plugins/acl/aclinit.c
new file mode 100644
index 00000000..21e54337
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclinit.c
@@ -0,0 +1,537 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+static int __aclinit__RegisterLases(void);
+static int __aclinit__RegisterAttributes(void);
+static int __aclinit_handler(Slapi_Entry *e, void *callback_data);
+
+/***************************************************************************
+*
+* aclinit_main()
+* Main routine which is called at the server boot up time.
+*
+* 1) Reads all the ACI entries from the database and creates
+* the ACL list.
+* 2) Registers all the LASes and the GetAttrs supported by the DS.
+* 3) Generates anonymous profiles.
+* 4) Registers proxy control
+* 5) Creates aclpb pool
+*
+* Input:
+* None.
+*
+* Returns:
+* 0 -- no error
+* 1 -- Error
+*
+* Error Handling:
+* If any error found during the ACL generation, error is logged.
+*
+**************************************************************************/
+static int acl_initialized = 0;
+int
+aclinit_main()
+{
+ char *cookie = NULL;
+ Slapi_PBlock *pb;
+ int rv;
+ Slapi_DN *sdn;
+ void *node;
+
+ if (acl_initialized) {
+ /* There is no need to do anything more */
+ return 0;
+ }
+
+ /* Initialize the LIBACCESS ACL library */
+ if (ACL_Init() != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "ACL Library Initialization failed\n",0,0,0);
+ return 1;
+ }
+
+ /* register all the LASes supported by the DS */
+ if (ACL_ERR == __aclinit__RegisterLases()) {
+ /* Error is already logged */
+ return 1;
+ }
+
+ /* Register all the Attrs */
+ if (ACL_ERR == __aclinit__RegisterAttributes()) {
+ /* Error is already logged */
+ return 1;
+ }
+
+ /*
+ * Register to get backend state changes so we can add/remove
+ * acis from backends that come up and go down.
+ */
+
+ slapi_register_backend_state_change((void *) NULL, acl_be_state_change_fnc);
+
+
+ /* register the extensions */
+ /* ONREPL Moved to the acl_init function because extensions
+ need to be registered before any operations are issued
+ if ( 0 != acl_init_ext() ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the extensions\n");
+ return 1;
+ } */
+
+ /* create the mutex array */
+ if ( 0 != aclext_alloc_lockarray ( ) ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to create the mutext array\n");
+ return 1;
+ }
+
+ /* Allocate the pool */
+ if ( 0 != acl_create_aclpb_pool () ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to create the acl private pool\n");
+ return 1;
+ }
+
+ /*
+ * Now read all the ACLs from all the backends and put it
+ * in a list
+ */
+ /* initialize the ACLLIST sub-system */
+ if ( 0 != (rv = acllist_init ( ))) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the plugin:%d\n", rv );
+ return 1;
+ }
+
+ /* Initialize the anonymous profile i.e., generate it */
+ rv = aclanom_init ();
+
+ pb = slapi_pblock_new();
+
+ /*
+ * search for the aci_attr_type attributes of all entries.
+ *
+ * slapi_get_fist_suffix() and slapi_get_next_suffix() do not return the
+ * rootdse entry so we search for acis in there explicitly here.
+ */
+
+ sdn = slapi_sdn_new_dn_byval("");
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching for all acis(scope base) at suffix ''\n");
+ aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name*/
+ LDAP_SCOPE_BASE, ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ slapi_sdn_free(&sdn);
+
+ sdn = slapi_get_first_suffix( &node, 1 );
+ while (sdn)
+ {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching for all acis(scope subtree) at suffix '%s'\n",
+ slapi_sdn_get_dn(sdn) );
+ aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name*/
+ LDAP_SCOPE_SUBTREE, ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ sdn = slapi_get_next_suffix( &node, 1 );
+ }
+
+ /* Initialize it. */
+ acl_initialized = 1;
+
+ /* generate the signatures */
+ acl_set_aclsignature ( aclutil_gen_signature ( 100 ) );
+
+ /* Initialize the user-group cache */
+ rv = aclgroup_init ( );
+
+ aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
+
+ /* Register both of the proxied authorization controls (version 1 and 2) */
+ slapi_register_supported_control( LDAP_CONTROL_PROXYAUTH,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN
+ | SLAPI_OPERATION_EXTENDED );
+ slapi_register_supported_control( LDAP_CONTROL_PROXIEDAUTH,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN
+ | SLAPI_OPERATION_EXTENDED );
+
+ slapi_pblock_destroy ( pb );
+ return 0;
+}
+/*
+ * This routine is the one that scans for acis and either adds them
+ * to the internal cache (op==ACL_ADD_ACIS) or deletes them
+ * (op==ACL_REMOVE_ACIS).
+ *
+ * If thisbeonly is 0 the search
+ * is conducted on the base with the specifed scope and be_name is ignored.
+ * This is used at startup time where we iterate over all suffixes, searching
+ * for all the acis in the DIT to load the ACL cache.
+ *
+ * If thisbeonly is 1 then then a be_name must be specified.
+ * In this case we will search in that backend ONLY.
+ * This is used in the case where a backend is turned on and off--in this
+ * case we only want to add/remove the acis in that particular backend and
+ * not for example in any backends below that one.
+*/
+
+int
+aclinit_search_and_update_aci ( int thisbeonly, const Slapi_DN *base,
+ char *be_name, int scope, int op,
+ acl_lock_flag_t lock_flag )
+{
+ char *attrs[2] = { "aci", NULL };
+ /* Tell __aclinit_handler whether it's an add or a delete */
+ int any_error = op;
+ Slapi_PBlock *aPb;
+ LDAPControl **ctrls=NULL;
+ int retval;
+ struct berval *bval;
+ aclinit_handler_callback_data_t call_back_data;
+
+ PR_ASSERT( lock_flag == DONT_TAKE_ACLCACHE_WRITELOCK ||
+ lock_flag == DO_TAKE_ACLCACHE_WRITELOCK);
+
+ if ( thisbeonly && be_name == NULL) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: This be_name must be specified.\n", 0, 0, 0);
+ return -1;
+ }
+
+
+ /*
+ * We need to explicitly request (objectclass=ldapsubentry)
+ * in order to get all the subentry acis too.
+ * Note that subentries can be added under subentries (although its not
+ * recommended) so that
+ * there may be non-trivial acis under a subentry.
+ */
+
+ /* Use new search internal API */
+ /* and never retrieve aci from a remote server */
+ aPb = slapi_pblock_new ();
+
+ /*
+ * Set up the control to say "Only get acis from this Backend--
+ * there may be more backends under this one.
+ */
+
+ if ( thisbeonly ) {
+
+ bval = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ bval->bv_len = strlen(be_name) + 1;
+ bval->bv_val = slapi_ch_strdup(be_name);
+
+ ctrls = (LDAPControl **)slapi_ch_calloc( 2, sizeof(LDAPControl *));
+ ctrls[0] = NULL;
+ ctrls[1] = NULL;
+
+ retval = slapi_build_control_from_berval(
+ MTN_CONTROL_USE_ONE_BACKEND_OID,
+ bval,
+ 1 /* is critical */,
+ ctrls);
+
+ }
+
+ slapi_search_internal_set_pb ( aPb,
+ slapi_sdn_get_dn(base),
+ scope,
+ "(|(aci=*)(objectclass=ldapsubentry))",
+ attrs,
+ 0 /* attrsonly */,
+ ctrls /* controls: SLAPI_ARGCONTROLS */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions : get local aci only */);
+
+ if (thisbeonly) {
+ slapi_pblock_set(aPb, SLAPI_REQCONTROLS, ctrls);
+ }
+
+ call_back_data.op = op;
+ call_back_data.retCode = 0;
+ call_back_data.lock_flag = lock_flag;
+
+ slapi_search_internal_callback_pb(aPb,
+ &call_back_data /* callback_data */,
+ NULL/* result_callback */,
+ __aclinit_handler,
+ NULL /* referral_callback */);
+
+ if (thisbeonly) {
+ slapi_ch_free((void **)&bval);
+ }
+
+ /*
+ * This frees the control oid, the bv_val and the control itself and the
+ * ctrls array mem by caling ldap_controls_free()--so we
+ * don't need to do it ourselves.
+ */
+ slapi_pblock_destroy (aPb);
+
+ return call_back_data.retCode;
+
+}
+
+/***************************************************************************
+*
+* __aclinit_handler
+*
+* For each entry, finds if there is any ACL in thet entry. If there is
+* then the ACL is processed and stored in the ACL LIST.
+*
+*
+* Input:
+*
+*
+* Returns:
+* None.
+*
+* Error Handling:
+* If any error found during the ACL generation, the ACL is
+* logged. Also, set in the callback_data so that caller can act upon it.
+*
+**************************************************************************/
+static int
+__aclinit_handler ( Slapi_Entry *e, void *callback_data)
+{
+ Slapi_Attr *attr;
+ aclinit_handler_callback_data_t *call_back_data =
+ (aclinit_handler_callback_data_t*)callback_data;
+ Slapi_DN *e_sdn;
+ int rv;
+ Slapi_Value *sval=NULL;
+
+ call_back_data->retCode = 0; /* assume success--if there's an error we overwrite it */
+ if (e != NULL) {
+
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ /*
+ * Take the write lock around all the mods--so that
+ * other operations will see the acicache either before the whole mod
+ * or after but not, as it was before, during the mod.
+ * This is in line with the LDAP concept of the operation
+ * on the whole entry being the atomic unit.
+ *
+ */
+
+ if ( call_back_data->op == ACL_ADD_ACIS ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding acis for entry '%s'\n", slapi_sdn_get_dn(e_sdn));
+ slapi_entry_attr_find ( e, aci_attr_type, &attr );
+
+ if ( attr ) {
+
+ const struct berval *attrValue;
+
+ int i;
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_LOCK();
+ }
+ i= slapi_attr_first_value ( attr, &sval );
+ while(i != -1) {
+ attrValue = slapi_value_get_berval(sval);
+
+ if ( 0 != (rv=acllist_insert_aci_needsLock (e_sdn, attrValue))) {
+ aclutil_print_err(rv, e_sdn, attrValue, NULL);
+
+ /* We got an error; Log it and then march along */
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: This (%s) ACL will not be considered for evaluation"
+ " because of syntax errors.\n",
+ attrValue->bv_val ? attrValue->bv_val: "NULL", 0, 0);
+ call_back_data->retCode = rv;
+ }
+ i= slapi_attr_next_value( attr, i, &sval );
+ }/* while */
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ }
+ } else if (call_back_data->op == ACL_REMOVE_ACIS) {
+
+ /* Here we are deleting the acis. */
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Removing acis\n");
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_LOCK();
+ }
+ if ( 0 != (rv=acllist_remove_aci_needsLock(e_sdn, NULL))) {
+ aclutil_print_err(rv, e_sdn, NULL, NULL);
+
+ /* We got an error; Log it and then march along */
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: ACls not deleted from %s\n",
+ e_sdn, 0, 0);
+ call_back_data->retCode = rv;
+ }
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ }
+
+ }
+
+ /*
+ * If we get here it's success.
+ * The call_back_data->error is the error code that counts as it's the
+ * one that the original caller will see--this routine is called off a callbacl.
+ */
+
+ return ACL_FALSE; /* "local" error code--it's 0 */
+}
+/***************************************************************************
+*
+* __acl__RegisterAttributes
+*
+* Register all the attributes supported by the DS.
+*
+* Input:
+* None.
+*
+* Returns:
+* ACL_OK - No error
+* ACL_ERR - in case of errror
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclinit__RegisterAttributes(void)
+{
+
+ ACLMethod_t methodinfo;
+ NSErr_t errp;
+ int rv;
+
+ memset (&errp, 0, sizeof(NSErr_t));
+
+ rv = ACL_MethodRegister(&errp, DS_METHOD, &methodinfo);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register the methods\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_MethodSetDefault (&errp, methodinfo);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Set the default method\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_IP, DS_LASIpGetter,
+ methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register Attr ip\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_DNS, DS_LASDnsGetter,
+ methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register Attr dns\n", 0,0,0);
+ return ACL_ERR;
+ }
+ return ACL_OK;
+}
+
+/***************************************************************************
+*
+* __acl__RegisterLases
+* Register all the LASes supported by the DS.
+*
+* The DS doesnot support user/group. We have defined our own LAS
+* so that we can display/print an error when the LAS is invoked.
+* Input:
+* None.
+*
+* Returns:
+* ACL_OK - No error
+* ACL_ERR - in case of errror
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclinit__RegisterLases(void)
+{
+
+ if (ACL_LasRegister(NULL, DS_LAS_USER, (LASEvalFunc_t) DS_LASUserEval,
+ (LASFlushFunc_t) NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USER Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUP, (LASEvalFunc_t) DS_LASGroupEval,
+ (LASFlushFunc_t) NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUP Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUPDN, (LASEvalFunc_t)DS_LASGroupDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUPDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_ROLEDN, (LASEvalFunc_t)DS_LASRoleDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register ROLEDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERDN, (LASEvalFunc_t)DS_LASUserDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERDNATTR,
+ (LASEvalFunc_t)DS_LASUserDnAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERDNATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_AUTHMETHOD,
+ (LASEvalFunc_t)DS_LASAuthMethodEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register CLIENTAUTHTYPE Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUPDNATTR,
+ (LASEvalFunc_t)DS_LASGroupDnAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUPDNATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERATTR,
+ (LASEvalFunc_t)DS_LASUserAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ return ACL_OK;
+}
diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c
new file mode 100644
index 00000000..0179b0e6
--- /dev/null
+++ b/ldap/servers/plugins/acl/acllas.c
@@ -0,0 +1,3848 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <ipfstruct.h>
+#include "acl.h"
+
+/*
+ A word on this file:
+
+ The various routines here implement each component of the subject of an aci
+ eg. "groupdn", "userdn","roledn", "userattr" etc.
+ They are responsible for evaluating each individual keyword not for doing
+ the boolean combination of these keywords, nor for combining multiple
+ allow()/deny() statements--that's libaccess's job.
+ For example, for "groupdn", DS_LASGroupDnEval might have to evaluate
+ something like this:
+
+ "groupdn = "ldap:///cn=G1,o=sun.com || ldap:///cn=G2,o=sun.com"
+
+ The "=" here may be "!=" as well and these routines take care of the
+ comparator.
+
+ These rotuines get called via acl__TestRights(), which calls
+ ACL_EvalTestRights() a libaccess routine (the immediately calling routine is
+ ACLEvalAce() in oneeval.cpp).
+
+ They should return LAS_EVAL_TRUE, if that keyword component evaluates to
+ TRUE, LAS_EVAL_FALSE if it evaluates to FALSE and LAS_EVAL_FAIL if an
+ error occurrs during evaluation. Note that once any component of a subject
+ returns LAS_EVAL_FAIL, the evaluation in libaccess stops and the whole
+ subject does not match and that aci is not applied.
+*/
+
+/*
+
+ A word on three-valued logic:
+
+ In general when you do boolean combination of terms some of which
+ may evaluate to UNDEFINED then you need to define what the combination
+ means.
+ So, for example libaccess implements a scheme which once UNDEFINED
+ is returned for a term, it bales out of the
+ evaluation and the whole expression evaluates to UNDEFINED.
+ In this case the aci will not apply.
+ On the other hand LDAP filters (cf. rfc2251 4.5.1) say that for OR,
+ an expression will
+ evaluate to TRUE if any term is TRUE, even if some terms are UNDEFINED.
+ Other off the cuff options might be to redefine UNDEFINED to be FALSE,
+ or TRUE.
+
+ Which is best ?
+
+ Well it probably depends on exactly what is to decided based on the
+ evaluation of the logical expression. However, the final suggestion is
+ almost certainly
+ bad--you are unlikely to want to take an action based on an undefined
+ result and
+ defining UNDEFINED to be either TRUE or FALSE may result in the overall
+ expression
+ returning TRUE--a security hole. The only case this might work is if you
+ are dealing with restricted
+ expressions eg. terms may only be AND'ed togther--in this case defining
+ UNDEFINED to be FALSE would guarantee a result of FALSE.
+
+ The libaccess approach of returning UNDEFINED once an UNDEFINED is
+ encountered during
+ evaluation is not too bad--at least it guarantees that no aci will apply
+ based on an
+ undefined value. However, with an aci like this "...allow(all) A or B"
+ where A returned UNDEFINED, you might be disappointed not to receive the
+ rights if it was B that
+ was granting you the rights and evaluation of A, which has nothing to do
+ with you, returns UNDEFINED. In the case of an aci like
+ "...deny(all) A or B" then the same
+ situation is arguably a security hole. Note that this scheme also makes
+ the final result
+ dependent on the evaluation order and so if the evaluation engine does
+ anything fancy internally (eg. reordering the terms in an OR so that fast
+ to evaluate ones came first) then
+ this would need to be documented so that a user (or a tool) could look at
+ the external syntax and figure out the result of the evaluation.
+ Also it breaks commutivity and De Morgans law.
+
+ The LDAP filter scheme is starting to look good--it solves the problems of
+ the
+ libaccess approach, makes the final result of an expression independent of
+ the evaluation order and
+ gives you back commutivity of OR and AND. De Morgans is still broken, but
+ that's because of the asymmetry of behaviour of UNDEFINED with OR and AND.
+
+ So...?
+
+ For acis, in general it can look like this:
+
+ "...allow(rights)(LogicalCombinationofBindRule);
+ deny(LogicalCombinationOfBindRule)...."
+
+ A BindRule is one of the "userdn", "groupdn" or "userattr" things and it
+ can look like this:
+
+ "groupdn = "ldap:///cn=G1,o=sun.com || ldap:///cn=G2,o=sun.com"
+
+ The "=" here may be "!=" as well and these routines take care of the
+ comparator.
+
+ For "userattr" keywords a mutilvalued attribute amounts a logical OR of the
+ individual values. There is also a logical OR over the different levels
+ as specified by the "parent" keyword.
+
+ In fact there are three levels of logical combination:
+
+ 1. In the aclplugin:
+ The "||" and "!=" combinator for BindRule keywords like userdn and
+ groupdn.
+ The fact that for the "userattr" keyword, a mutilvalued attribute is
+ evaluated as "||". Same for the different levels.
+ 2. In libaccess:
+ The logical combination of BindRules.
+ 3. In libaccess:
+ The evaluation of multiple BindRules seperated by ";", which means OR.
+
+ The LDAP filter three-valued logic SHOULD be applied to each level but
+ here's the way it works right now:
+
+ 1. At this level it depends....
+
+ DS_LASIpGetter - get attr for IP -
+ returns ip address or LAS_EVAL_FAIL for error.
+ no logical combination.
+ DS_LASDnsGetter - get attr for DNS-
+ returns dns name or LAS_EVAL_FAIL for error
+ no logical combination.
+ DS_LASUserDnEval - LAS Evaluation for USERDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASGroupDnEval - LAS Evaluation for GROUPDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASRoleDnEval - LAS Evaluation for ROLEDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASUserDnAttrEval - LAS Evaluation for USERDNATTR -
+ three-valued logic
+ logical combination || (over specified attribute values and
+ parent keyword levels), !=
+ DS_LASAuthMethodEval - LAS Evaluation for AUTHMETHOD -
+ three-valued logic ( logical combinations: !=)
+ DS_LASGroupDnAttrEval - LAS Evaluation for GROUPDNATTR -
+ three-valued logic
+ logical combination || (over specified attribute values and
+ parent keyword levels), !=
+ DS_LASUserAttrEval - LAS Evaluation for USERATTR -
+ USER, GROUPDN and ROLEDN as above.
+ LDAPURL -- three-valued logic (logical combinations: || over
+ specified attribute vales, !=)
+ attrname#attrvalue -- three-valued logic, logical combination:!=
+
+ 2. The libaccess scheme applies at this level.
+ 3. The LDAP filter three-valued logic applies at this level.
+
+ Example of realistic, non-bizarre things that cause evaluation of a
+ BindRule to be undefined are exceeding some resource limits (nesting level,
+ lookthrough limit) in group membership evaluation, or trying to get ADD
+ permission from the "userattr" keyword at "parent" level 0.
+ Note that not everything that might be construed as an error needs to be
+ taken as UNDEFINED. For example, things like not finding a user or an
+ attribute in an entry can be defined away as TRUE or FALSE. eg. in an
+ LDAP filter (cn=rob) applied to an entry where cn is not present is FALSE,
+ not UNDEFINED. Similarly, if the number of levels in a parent keyword
+ exceeds the allowed limit, we just ignore the rest--though this
+ is a syntax error which should be detected at parse time.
+
+
+*/
+
+/* To get around warning: declared in ldapserver/lib/ldaputil/ldaputili.h */
+extern int ldapu_member_certificate_match (void* cert, const char* desc);
+
+/****************************************************************************/
+/* Defines, Constants, ande Declarations */
+/****************************************************************************/
+static char* const type_objectClass = "objectclass";
+static char* const filter_groups = "(|(objectclass=groupOfNames) (objectclass=groupOfUniqueNames)(objectclass=groupOfCertificates)(objectclass=groupOfURLs))";
+static char* const type_member = "member";
+static char* const type_uniquemember = "uniquemember";
+static char* const type_memberURL = "memberURL";
+static char* const type_memberCert = "memberCertificateDescription";
+
+/* cache strategy for groups */
+#define ACLLAS_CACHE_MEMBER_GROUPS 0x1
+#define ACLLAS_CACHE_NOT_MEMBER_GROUPS 0x2
+#define ACLLAS_CACHE_ALL_GROUPS 0x3
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int acllas__handle_group_entry(Slapi_Entry *, void *);
+static int acllas__user_ismember_of_group(struct acl_pblock *aclpb,
+ char* groupDN,
+ char* clientDN,
+ int cache_status,
+ CERTCertificate *clientCert);
+static int acllas__user_has_role( struct acl_pblock *aclpb,
+ Slapi_DN *roleDN, Slapi_DN *clientDn);
+static int acllas__add_allgroups (Slapi_Entry* e, void *callback_data);
+static int acllas__eval_memberGroupDnAttr (char *attrName,
+ Slapi_Entry *e,
+ char *n_clientdn,
+ struct acl_pblock *aclpb);
+static int acllas__verify_client (Slapi_Entry* e, void *callback_data);
+static char* acllas__dn_parent( char *dn, int level);
+static int acllas__get_members (Slapi_Entry* e, void *callback_data);
+static int acllas__client_match_URL (struct acl_pblock *aclpb,
+ char *n_dn, char *url );
+static int acllas__handle_client_search (Slapi_Entry *e, void *callback_data);
+static int __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth, char *lasType, char *lasName, lasInfo *linfo);
+int
+aclutil_evaluate_macro( char * user, lasInfo *lasinfo,
+ acl_eval_types evalType );
+static int
+acllas_eval_one_user( struct acl_pblock *aclpb,
+ char * clientDN, char *userKeyword);
+static int
+acllas_eval_one_group(char *group, lasInfo *lasinfo);
+static int
+acllas_eval_one_role(char *role, lasInfo *lasinfo);
+static char **
+acllas_replace_dn_macro( char *rule, char *matched_val, lasInfo *lasinfo);
+static char **
+acllas_replace_attr_macro( char *rule, lasInfo *lasinfo);
+static int
+acllas_eval_one_target_filter( char * str, Slapi_Entry *e);
+
+/****************************************************************************/
+
+int
+DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg)
+{
+
+ struct acl_pblock *aclpb = NULL;
+ IPAddr_t ip=0;
+ PRNetAddr client_praddr;
+ struct in_addr client_addr;
+ int rv;
+
+
+ rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rv != LAS_EVAL_TRUE || ( NULL == aclpb )) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASIpGetter:Unable to get the ACLPB(%d)\n", rv,0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR,
+ &client_praddr ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+
+ if ( !PR_IsNetAddrType(&client_praddr, PR_IpAddrV4Mapped) ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Client address is IPv6. ACLs only support IPv4 addresses so far.\n");
+ return( LAS_EVAL_FAIL );
+ }
+
+ client_addr.s_addr = client_praddr.ipv6.ip.pr_s6_addr32[3];
+
+ ip = (IPAddr_t) ntohl( client_addr.s_addr );
+ rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)ip, NULL);
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning client ip address '%s'\n",
+ (slapi_is_loglevel_set(SLAPI_LOG_ACL) ? inet_ntoa(client_addr) : ""));
+
+ return LAS_EVAL_TRUE;
+
+}
+
+/*
+ * This is called from the libaccess code when it needs to find a dns name.
+ * It's called from ACL_GetAttribute() when it finds that ACL_ATTR_DNS is
+ * not already part of the proplist.
+ *
+*/
+
+int
+DS_LASDnsGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg)
+{
+ struct acl_pblock *aclpb = NULL;
+ PRNetAddr client_praddr;
+ PRHostEnt *hp;
+ char *dnsName = NULL;
+ int rv;
+ struct berval **clientDns;
+
+
+ rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rv != LAS_EVAL_TRUE || ( NULL == aclpb )) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASDnsGetter:Unable to get the ACLPB(%d)\n", rv,0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CLIENT_DNS, &clientDns ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+
+ /*
+ * If the client hostname has already been put into the pblock then
+ * use that. Otherwise we work it out and add it ourselves.
+ * This info is connection-lifetime so with multiple operaitons on the same
+ * connection we will only do the calculation once.
+ *
+ * rbyrneXXX surely this code would be better in connection.c so
+ * the name would be just there waiting for us, and everyone else.
+ *
+ */
+
+ if ( clientDns && clientDns[0] != NULL && clientDns[0]->bv_val ) {
+ dnsName = clientDns[0]->bv_val;
+ } else {
+ struct berval **dnsList;
+ char buf[PR_NETDB_BUF_SIZE];
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR, &client_praddr ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+ hp = (PRHostEnt *)slapi_ch_malloc( sizeof(PRHostEnt) );
+ if ( PR_GetHostByAddr( &(client_praddr), (char *)buf, sizeof(buf), hp ) == PR_SUCCESS ) {
+ if ( hp->h_name != NULL ) {
+ dnsList = (struct berval**)
+ slapi_ch_calloc (1, sizeof(struct berval*) * (1 + 1));
+ *dnsList = (struct berval*)
+ slapi_ch_calloc ( 1, sizeof(struct berval));
+ dnsName = (*dnsList)->bv_val = slapi_ch_strdup( hp->h_name );
+ (*dnsList)->bv_len = strlen ( (*dnsList)->bv_val );
+ slapi_pblock_set( aclpb->aclpb_pblock, SLAPI_CLIENT_DNS, &dnsList );
+ }
+ }
+ slapi_ch_free( (void **)&hp );
+ }
+
+ if ( NULL == dnsName ) return LAS_EVAL_FAIL;
+
+ rv = PListInitProp(subject, 0, ACL_ATTR_DNS, dnsName, NULL);
+ if (rv < 0) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASDnsGetter:Couldn't set the DNS property(%d)\n", rv );
+ return LAS_EVAL_FAIL;
+ }
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "DNS name: %s\n", dnsName );
+ return LAS_EVAL_TRUE;
+
+}
+/***************************************************************************/
+/* New LASes */
+/* */
+/* 1. user, groups. -- stubs to report errors. Not supported. */
+/* 2. userdn */
+/* 3. groupdn */
+/* 4. userdnattr */
+/* 5. authmethod */
+/* 6. groupdnattr */
+/* 7. roledn */
+/* */
+/* */
+/***************************************************************************/
+int
+DS_LASUserEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "User LAS is not supported in the ACL\n",0,0,0);
+
+ return LAS_EVAL_INVALID;
+}
+
+int
+DS_LASGroupEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Group LAS is not supported in the ACL\n",0,0,0);
+
+ return LAS_EVAL_INVALID;
+}
+
+/***************************************************************************
+*
+* DS_LASUserDnEval
+* Evaluate the "userdn" LAS. See if the user has rights.
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASUserDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *users = NULL;
+ char *s_user, *user = NULL;
+ char *ptr = NULL;
+ char *end_dn = NULL;
+ char *n_edn = NULL;
+ char *parent_dn = NULL;
+ int matched;
+ int rc;
+ short len;
+ char *s = NULL;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERDN, "DS_LASUserDnEval", &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ users = slapi_ch_strdup(attr_pattern);
+ user = users;
+ matched = ACL_FALSE;
+
+ /* check if the clientdn is one of the users */
+ while(user != 0 && *user != 0 && matched != ACL_TRUE ) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(user))
+ LDAP_UTF8INC(user);
+
+ /* Now we must see the userdn in the following
+ ** formats:
+ **
+ ** The following formats are supported:
+ **
+ ** 1. The DN itself:
+ ** allow (read) userdn = "ldap:///cn=prasanta, ..."
+ **
+ ** 2. keyword SELF:
+ ** allow (write)
+ ** userdn = "ldap:///self"
+ **
+ ** 3. Pattern:
+ ** deny (read) userdn = "ldap:///cn=*, o=netscape, c = us";
+ **
+ ** 4. Anonymous user
+ ** deny (read, write) userdn = "ldap:///anyone"
+ **
+ ** 5. All users (All authenticated users)
+ ** allow (search) ** userdn = "ldap:///all"
+ ** 6. parent "ldap:///parent"
+ ** 7. Synamic users using the URL
+ **
+ **
+ ** DNs must be separated by "||". Ex:
+ ** allow (read)
+ ** userdn = "ldap:///DN1 || ldap:///DN2"
+ */
+
+
+ /* The DN is now "ldap:///DN"
+ ** remove the "ldap:///" part
+ */
+ if (strncasecmp (user, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ s_user = user;
+ user += LDAP_URL_prefix_len;
+
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASUserDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( user, ebuf ), 0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Now we have the starting point of the "userdn" */
+ if ((end_dn = strstr(user, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+ /* Now user is a null terminated string */
+
+ if (*user) {
+ while(ldap_utf8isspace(user))
+ LDAP_UTF8INC(user);
+ /* ignore trailing whitespace */
+ len = strlen(user);
+ ptr = user+len-1;
+ while(ldap_utf8isspace(ptr)){ *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Check , if the user is a anonymous user. In that case
+ ** We must find the rule "ldap:///anyone"
+ */
+ if (lasinfo.anomUser) {
+ if (strcasecmp(user, "anyone") == 0 ) {
+ /* matches -- anonymous user */
+ matched = ACL_TRUE;
+ break;
+ }
+ } else {
+ /* URL format */
+
+ if ((s = strstr (user, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (user, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (user, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( s_user, &lasinfo,
+ ACL_EVAL_USER);
+ if (matched == ACL_TRUE) {
+ break;
+ }
+
+ } else if ((s = strchr (user, '?'))!= NULL) {
+ /* URL format */
+ if (acllas__client_match_URL ( lasinfo.aclpb, lasinfo.clientDn,
+ s_user) == ACL_TRUE) {
+ matched = ACL_TRUE;
+ break;
+ }
+ } else if (strcasecmp(user, "anyone") == 0 ) {
+ /* Anyone means anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if (strcasecmp(user, "self") == 0) {
+ if (n_edn == NULL) {
+ n_edn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ }
+ if (slapi_utf8casecmp((ACLUCHP)lasinfo.clientDn, (ACLUCHP)n_edn) == 0)
+ matched = ACL_TRUE;
+ break;
+ } else if (strcasecmp(user, "parent") == 0) {
+ if (n_edn == NULL) {
+ n_edn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ }
+ /* get the parent */
+ parent_dn = slapi_dn_parent(n_edn);
+ if (parent_dn &&
+ slapi_utf8casecmp ((ACLUCHP)lasinfo.clientDn, (ACLUCHP)parent_dn) == 0)
+ matched = ACL_TRUE;
+
+ if (parent_dn) slapi_ch_free ( (void **) &parent_dn );
+ break;
+ } else if (strcasecmp(user, "all") == 0) {
+ /* matches -- */
+ matched = ACL_TRUE;
+ break;
+ } else if (strchr(user, '*')) {
+ char line[200];
+ char *lineptr = &line[0];
+ char *newline = NULL;
+ int lenu = 0;
+ Slapi_Filter *f = NULL;
+ char *tt;
+ int filterChoice;
+
+ /*
+ ** what we are doing is faking the str2simple()
+ ** function with a "userdn = "user")
+ */
+ for (tt = user; *tt; tt++)
+ *tt = TOLOWER ( *tt );
+
+ if ((lenu = strlen(user)) > 190) { /* 200 - 9 for "(userdn=%s)" */
+ newline = slapi_ch_malloc(lenu + 10);
+ lineptr = newline;
+ }
+
+ sprintf (lineptr, "(userdn=%s)", user);
+ if ((f = slapi_str2filter (lineptr)) == NULL) {
+ if (newline) slapi_ch_free((void **) &newline);
+ /* try the next one */
+ break;
+ }
+ if (newline) slapi_ch_free((void **) &newline);
+ filterChoice = slapi_filter_get_choice ( f );
+
+ if (( filterChoice != LDAP_FILTER_SUBSTRINGS) &&
+ ( filterChoice != LDAP_FILTER_PRESENT)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserDnEval:Error in gen. filter(%s)\n", user);
+ }
+ if ((rc = acl_match_substring( f,
+ lasinfo.clientDn,
+ 1 /*exact match */)
+ ) == ACL_TRUE) {
+ matched = ACL_TRUE;
+ slapi_filter_free(f,1);
+ break;
+ }
+ if (rc == ACL_ERR) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserDnEval:Error in matching patteren(%s)\n",
+ user,0,0);
+ }
+ slapi_filter_free(f,1);
+ } else {
+ /* Must be a simple dn then */
+ if (slapi_utf8casecmp((ACLUCHP)lasinfo.clientDn,
+ (ACLUCHP)slapi_dn_normalize(user)) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ }
+ }
+
+ if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another user will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ /* Nothing matched -- try the next DN */
+ user = end_dn;
+ } /* end of while */
+
+ slapi_ch_free ( (void **) &users);
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for userdn evaluation.\n");
+ }
+
+ return rc;
+}
+
+/***************************************************************************
+*
+* DS_LASGroupDnEval
+*
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return code
+* If the client is in any of the groups mentioned this groupdn keywrod
+* then returns LAS_EVAL_TRUE, if he's not in any LAS_EVAL_FALSE.
+* If any of the membership evaluations fail, then it goes on to evaluate the
+* others.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASGroupDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *groups;
+ char *groupName;
+ char *ptr;
+ char *end_dn;
+ int matched;
+ int rc;
+ int len;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ int any_group = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ /* the setup should not fail under normal operation */
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_GROUPDN, "DS_LASGroupDnEval", &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ groups = slapi_ch_strdup(attr_pattern);
+ groupName = groups;
+ matched = ACL_FALSE;
+
+ /* check if the groupdn is one of the users */
+ while(groupName != 0 && *groupName != 0 && matched != ACL_TRUE) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(groupName))
+ LDAP_UTF8INC(groupName);
+
+ /*
+ ** The syntax allowed for the groupdn is
+ **
+ ** Example:
+ ** groupdn = "ldap:///dn1 || ldap:///dn2";
+ **
+ */
+
+ if (strncasecmp (groupName, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ groupName += LDAP_URL_prefix_len;
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASGroupDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( groupName, ebuf ),0,0);
+ }
+
+ /* Now we have the starting point of the "groupdn" */
+ if ((end_dn = strstr(groupName, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+ if (*groupName) {
+ while(ldap_utf8isspace(groupName))
+ LDAP_UTF8INC(groupName);
+ /* ignore trailing whitespace */
+ len = strlen(groupName);
+ ptr = groupName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Now we have the DN of the group. Evaluate the "clientdn"
+ ** and see if the user is a member of the group.
+ */
+ if (0 == (strcasecmp(groupName, "anyone"))) {
+ any_group = 1;
+ }
+
+ if (any_group) {
+ /* anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if ( lasinfo.anomUser &&
+ (lasinfo.aclpb->aclpb_clientcert == NULL) && (!any_group)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Group not evaluated(%s)\n", groupName);
+ break;
+ } else {
+ char *s;
+
+ if ((s = strstr (groupName, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (groupName, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (groupName, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( groupName, &lasinfo,
+ ACL_EVAL_GROUP);
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASGroupDnEval: Param group name:%s\n",
+ groupName);
+ } else {/* normal evaluation */
+
+ matched = acllas_eval_one_group( groupName, &lasinfo);
+
+ }
+
+ if ( matched == ACL_TRUE ) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Nothing matched -- try the next DN */
+ groupName = end_dn;
+
+ } /* end of while */
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for groupdn evaluation.\n");
+ }
+
+ slapi_ch_free ((void**) &groups);
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASRoleDnEval
+*
+*
+* Input:
+* attr_name The string "roledn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A "||" sperated list of roles
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASRoleDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *roles;
+ char *role;
+ char *ptr;
+ char *end_dn;
+ int matched;
+ int rc;
+ int len;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ int any_role = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_ROLEDN, "DS_LASRoleDnEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FALSE;
+ }
+
+
+ roles = slapi_ch_strdup(attr_pattern);
+ role = roles;
+ matched = ACL_FALSE;
+
+ /* check if the roledn is one of the users */
+ while(role != 0 && *role != 0 && matched != ACL_TRUE) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(role))
+ LDAP_UTF8INC(role);
+
+ /*
+ ** The syntax allowed for the roledn is
+ **
+ ** Example:
+ ** roledn = "ldap:///roledn1 || ldap:///roledn2";
+ **
+ */
+
+ if (strncasecmp (role, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ role += LDAP_URL_prefix_len;
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASRoleDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( role, ebuf ),0,0);
+ }
+
+ /* Now we have the starting point of the "roledn" */
+ if ((end_dn = strstr(role, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+
+ if (*role) {
+ while(ldap_utf8isspace(role))
+ LDAP_UTF8INC(role);
+ /* ignore trailing whitespace */
+ len = strlen(role);
+ ptr = role+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Now we have the DN of the role. Evaluate the "clientdn"
+ ** and see if the user has this role.
+ */
+ if (0 == (strcasecmp(role, "anyone"))) {
+ any_role = 1;
+ }
+
+ if (any_role) {
+ /* anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if ( lasinfo.anomUser &&
+ (lasinfo.aclpb->aclpb_clientcert == NULL) && (!any_role)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Role not evaluated(%s) for anon user\n", role);
+ break;
+ } else {
+
+ /* Take care of param strings */
+
+ char *s;
+
+ if ((s = strstr (role, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (role, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (role, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( role, &lasinfo,
+ ACL_EVAL_ROLE);
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASRoleDnEval: Param role name:%s\n",
+ role);
+ } else {/* normal evaluation */
+
+ matched = acllas_eval_one_role( role, &lasinfo);
+
+ }
+
+ if ( matched == ACL_TRUE ) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another role will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Nothing matched -- try the next DN */
+ role = end_dn;
+
+ } /* end of while */
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for roledn evaluation.\n");
+ }
+
+ slapi_ch_free ((void**) &roles);
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASUserDnAttrEval
+*
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+struct userdnattr_info {
+ char *attr;
+ int result;
+ char *clientdn;
+};
+#define ACLLAS_MAX_LEVELS 10
+int
+DS_LASUserDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *n_currEntryDn = NULL;
+ char *s_attrName, *attrName;
+ char *ptr;
+ int matched;
+ int rc, len, i;
+ char *val;
+ Slapi_Attr *a;
+ int levels[ACLLAS_MAX_LEVELS];
+ int numOflevels =0;
+ struct userdnattr_info info;
+ char *attrs[2] = { LDAP_ALL_USER_ATTRS, NULL };
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERDNATTR, "DS_LASUserDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /*
+ ** The userdnAttr syntax is
+ ** userdnattr = <attribute> or
+ ** userdnattr = parent[0,2,4].attribute"
+ ** Ex:
+ ** userdnattr = manager; or
+ ** userdnattr = "parent[0,2,4].manager";
+ **
+ ** Here 0 means current level, 2 means grandfather and
+ ** 4 (great great grandfather)
+ **
+ ** The function of this LAS is to compare the value of the
+ ** attribute in the Slapi_Entry with the "userdn".
+ **
+ ** Ex: userdn: "cn=prasanta, o= netscape, c= us"
+ ** and in the Slapi_Entry the manager attribute has
+ ** manager = <value>. Compare the userdn with manager.value to
+ ** determine the result.
+ **
+ */
+ s_attrName = attrName = slapi_ch_strdup (attr_pattern);
+
+ /* ignore leading/trailing whitespace */
+ while(ldap_utf8isspace(attrName)) LDAP_UTF8INC(attrName);
+ len = strlen(attrName);
+ ptr = attrName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+
+ /* See if we have a parent[2].attr" rule */
+ if ( (ptr = strstr(attrName, "parent[")) != NULL) {
+ char *word, *str, *next;
+
+ numOflevels = 0;
+ n_currEntryDn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ str = attrName;
+
+ word = ldap_utf8strtok_r(str, "[],. ",&next);
+ /* The first word is "parent[" and so it's not important */
+
+ while ((word= ldap_utf8strtok_r(NULL, "[],.", &next)) != NULL) {
+ if (ldap_utf8isdigit(word)) {
+ while (word && ldap_utf8isspace(word)) LDAP_UTF8INC(word);
+ if (numOflevels < ACLLAS_MAX_LEVELS)
+ levels[numOflevels++] = atoi (word);
+ else {
+ /*
+ * Here, ignore the extra levels..it's really
+ * a syntax error which should have been ruled out at parse time
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASUserDnattr: Exceeded the ATTR LIMIT:%d: Ignoring extra levels\n",
+ ACLLAS_MAX_LEVELS);
+ }
+ } else {
+ /* Must be the attr name. We can goof of by
+ ** having parent[1,2,a] but then you have to be
+ ** stupid to do that.
+ */
+ char *p = word;
+ if (*--p == '.') {
+ attrName = word;
+ break;
+ }
+ }
+ }
+ info.attr = attrName;
+ info.clientdn = lasinfo.clientDn;
+ info.result = 0;
+ } else {
+ levels[0] = 0;
+ numOflevels = 1;
+
+ }
+
+ /* No attribute name specified--it's a syntax error and so undefined */
+ if (attrName == NULL ) {
+ slapi_ch_free ( (void**) &s_attrName);
+ return LAS_EVAL_FAIL;
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"Attr:%s\n" , attrName, 0,0);
+ matched = ACL_FALSE;
+ for (i=0; i < numOflevels; i++) {
+ if ( levels[i] == 0 ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int j;
+
+ /*
+ * For the add operation, the resource itself (level 0)
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ *
+ */
+
+ if ( lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: userdnAttr does not allow ADD permission at level 0.\n");
+ got_undefined = 1;
+ continue;
+ }
+ slapi_entry_attr_find( lasinfo.resourceEntry, attrName, &a);
+ if ( NULL == a ) continue;
+ j= slapi_attr_first_value ( a,&sval );
+ while ( j != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ /* Here if atleast 1 value matches then we are done.*/
+ val = slapi_dn_normalize (
+ slapi_ch_strdup( attrVal->bv_val));
+
+ if (slapi_utf8casecmp((ACLUCHP)val, (ACLUCHP)lasinfo.clientDn ) == 0) {
+ char ebuf [ BUFSIZ ];
+ /* Wow it matches */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "userdnAttr matches(%s, %s) level (%d)\n",
+ val,
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo.clientDn, ebuf),
+ 0);
+ matched = ACL_TRUE;
+ slapi_ch_free ( (void **) &val);
+ break;
+ }
+ slapi_ch_free ( (void**) &val);
+ j = slapi_attr_next_value ( a, j, &sval );
+ }
+ } else {
+ char *p_dn; /* parent dn */
+
+ p_dn = acllas__dn_parent (n_currEntryDn, levels[i]);
+ if (p_dn == NULL) continue;
+
+ /* use new search internal API */
+ {
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ p_dn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__verify_client,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy(aPb);
+ }
+
+ /*
+ * Currently info.result is boolean so
+ * we do not need to check for ACL_DONT_KNOW
+ */
+ if (info.result) {
+ matched = ACL_TRUE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "userdnAttr matches at level (%d)\n", levels[i]);
+ }
+ }
+ if (matched == ACL_TRUE) {
+ break;
+ }
+ }
+
+ slapi_ch_free ( (void **) &s_attrName);
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for userdnattr evaluation.\n");
+ }
+
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASAuthMethodEval
+*
+*
+* Input:
+* attr_name The string "authmethod" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASAuthMethodEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *attr;
+ char *ptr;
+ int len;
+ int matched;
+ int rc;
+ char *s = NULL;
+ lasInfo lasinfo;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_AUTHMETHOD, "DS_LASAuthMethodEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ attr = attr_pattern;
+
+ matched = ACL_FALSE;
+ /* ignore leading whitespace */
+ s = strstr (attr, SLAPD_AUTH_SASL);
+ if ( s) {
+ s +=4;
+ attr = s;
+ }
+
+ while(ldap_utf8isspace(attr)) LDAP_UTF8INC(attr);
+ len = strlen(attr);
+ ptr = attr+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASAuthMethodEval:authtype:%s authmethod:%s\n",
+ lasinfo.authType, attr);
+
+ /* None method means, we don't care -- otherwise we care */
+ if ((strcasecmp(attr, "none") == 0) ||
+ (strcasecmp(attr, lasinfo.authType) == 0)) {
+ matched = ACL_TRUE;
+ }
+
+ if ( matched == ACL_TRUE || matched == ACL_FALSE) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for authmethod evaluation.\n");
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+* Struct to evaluate and keep the current members being evaluated
+*
+* 0 1 2 3 4 5
+* member: [a,b,c,d,e,f]
+* c_idx may point to 2 i.e to "c" if "c" is being evaluated to
+* see if any of "c" members is the clientDN.
+* lu_idx points to the last used spot i.e 5.
+* lu_idx++ is the next free spot.
+*
+* We allocate ACLLAS_MAX_GRP_MEMBER ptr first and then we add if it
+* is required.
+*
+***************************************************************************/
+#define ACLLAS_MAX_GRP_MEMBER 50
+struct member_info
+{
+ char *member; /* member DN */
+ struct member_info *parent; /* parent of this member */
+} member_info;
+
+struct eval_info
+{
+ int result; /* result status */
+ char *userDN; /* client's normalized DN */
+ int c_idx; /* Index to the current member being processed */
+ int lu_idx; /* Index to the slot where the last member is stored */
+ char **member; /* mmebers list */
+ struct member_info **memberInfo;/* array of memberInfo */
+ CERTCertificate *clientCert; /* ptr to cert */
+ struct acl_pblock *aclpb; /*aclpblock */
+} eval_info;
+
+static void
+dump_member_info ( struct member_info *minfo, char *buf )
+{
+ if ( minfo )
+ {
+ if ( minfo->parent )
+ {
+ dump_member_info ( minfo->parent, buf );
+ }
+ else
+ {
+ strcat ( buf, "<nil>" );
+ }
+ strcat ( buf, "->" );
+ strcat ( buf, minfo->member );
+ }
+}
+
+static void
+dump_eval_info (char *caller, struct eval_info *info, int idx)
+{
+ char buf[1024];
+ int len;
+ int i;
+
+ if ( idx < 0 )
+ {
+ sprintf ( buf, "\nuserDN=\"%s\"\nmember=", info->userDN);
+ if (info->member)
+ {
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "\"%s\"", info->member );
+ }
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "\nmemberinfo[%d]-[%d]:", info->c_idx, info->lu_idx );
+ if ( info->memberInfo )
+ for (i = 0; i <= info->lu_idx; i++)
+ {
+ len = strlen(buf);
+ sprintf ( &buf[len], "\n [%d]: ", i );
+ dump_member_info ( info->memberInfo[i], buf );
+ }
+ slapi_log_error ( SLAPI_LOG_FATAL, NULL, "\n======== candidate member info in eval_info ========%s\n\n", buf );
+ }
+ else
+ {
+ sprintf (buf, "evaluated candidate [%d]=", idx);
+ switch (info->result)
+ {
+ case ACL_TRUE:
+ strcat (buf, "ACL_TRUE\n");
+ break;
+ case ACL_FALSE:
+ strcat (buf, "ACL_FALSE\n");
+ break;
+ case ACL_DONT_KNOW:
+ strcat (buf, "ACL_DONT_KNOW\n");
+ break;
+ default:
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "%d\n", info->result );
+ break;
+ }
+ dump_member_info ( info->memberInfo[idx], buf );
+ slapi_log_error ( SLAPI_LOG_FATAL, NULL, "%s\n", buf );
+ }
+}
+
+
+/***************************************************************************
+*
+* acllas__user_ismember_of_group
+*
+* Check if the user is a member of the group and nested groups..
+*
+* Input:
+* char *groupdn - DN of the group
+* char *clientDN - Dn of the client
+*
+* Returns:
+* ACL_TRUE - the user is a member of the group.
+* ACL_FALSE - Not a member
+* ACL_DONT_KNOW - Any errors eg. resource limits exceeded and we could
+* not compelte the evaluation.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acllas__user_ismember_of_group( struct acl_pblock *aclpb,
+ char* groupDN,
+ char* clientDN,
+ int cache_status,
+ CERTCertificate *clientCert)
+{
+
+
+ char *attrs[5];
+ char *currDN;
+ int i,j;
+ int result = ACL_FALSE;
+ struct eval_info info;
+ int nesting_level;
+ int numOfMembersAtCurrentLevel;
+ int numOfMembersVisited;
+ int totalMembersVisited;
+ int numOfMembers;
+ int max_nestlevel;
+ int max_memberlimit;
+ aclUserGroup *u_group;
+ char ebuf [ BUFSIZ ];
+ struct member_info *groupMember = NULL;
+ struct member_info *parentGroup = NULL;
+
+ /*
+ ** First, Let's look thru the cached list and determine if the client is
+ ** a member of the cached list of groups.
+ */
+ if ( (u_group = aclg_get_usersGroup ( aclpb , clientDN )) == NULL) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Failed to find/allocate a usergroup--aborting evaluation\n", 0, 0);
+ return(ACL_DONT_KNOW);
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluating user %s in group %s?\n",
+ clientDN, groupDN );
+
+ /* Before I start using, get a reader lock on the group cache */
+ aclg_lock_groupCache ( 1 /* reader */ );
+ for ( i= 0; i < u_group->aclug_numof_member_group; i++) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "-- In %s\n",
+ u_group->aclug_member_groups[i] );
+ if ( slapi_utf8casecmp((ACLUCHP)groupDN, (ACLUCHP)u_group->aclug_member_groups[i]) == 0){
+ aclg_unlock_groupCache ( 1 /* reader */ );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_TRUE\n");
+ return ACL_TRUE;
+ }
+ }
+
+ /* see if we know the client is not a member of a group. */
+ for ( i= 0; i < u_group->aclug_numof_notmember_group; i++) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "-- Not in %s\n",
+ u_group->aclug_notmember_groups[i] );
+ if ( slapi_utf8casecmp((ACLUCHP)groupDN, (ACLUCHP)u_group->aclug_notmember_groups[i]) == 0){
+ aclg_unlock_groupCache ( 1 /* reader */ );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_FALSE\n");
+ return ACL_FALSE;
+ }
+ }
+
+ /*
+ ** That means we didn't find the the group in the cache. -- we have to add it
+ ** so no need for READ lock - need to get a WRITE lock. We will get it just before
+ ** modifying it.
+ */
+ aclg_unlock_groupCache ( 1 /* reader */ );
+
+ /* Indicate the initialization handler -- this module will be
+ ** called by the backend to evaluate the entry.
+ */
+ info.result = ACL_FALSE;
+ if (clientDN && *clientDN != '\0')
+ info.userDN = clientDN;
+ else
+ info.userDN = NULL;
+
+ info.c_idx = 0;
+ info.memberInfo = (struct member_info **) slapi_ch_malloc (ACLLAS_MAX_GRP_MEMBER * sizeof(struct member_info *));
+ groupMember = (struct member_info *) slapi_ch_malloc ( sizeof (struct member_info) );
+ groupMember->member = slapi_ch_strdup(groupDN);
+ groupMember->parent = NULL;
+ info.memberInfo[0] = groupMember;
+ info.lu_idx = 0;
+
+ attrs[0] = type_member;
+ attrs[1] = type_uniquemember;
+ attrs[2] = type_memberURL;
+ attrs[3] = type_memberCert;
+ attrs[4] = NULL;
+
+ currDN = groupMember->member;
+
+ /* nesting level is 0 to begin with */
+ nesting_level = 0;
+ numOfMembersVisited = 0;
+ totalMembersVisited = 0;
+ numOfMembersAtCurrentLevel = 1;
+
+ if (clientCert)
+ info.clientCert = clientCert;
+ else
+ info.clientCert = NULL;
+ info.aclpb = aclpb;
+
+ max_memberlimit = aclpb->aclpb_max_member_sizelimit;
+ max_nestlevel = aclpb->aclpb_max_nesting_level;
+
+ /* dump_eval_info ( "acllas__user_ismember_of_group", &info, -1 ); */
+
+eval_another_member:
+
+ numOfMembers = info.lu_idx - info.c_idx;
+
+ /* Use new search internal API */
+ {
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+
+ /*
+ * This search may NOT be chained--we demand that group
+ * definition be local.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ currDN,
+ LDAP_SCOPE_BASE,
+ filter_groups,
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_group_entry,
+ NULL /* referral_callback */);
+
+ if ( info.result == ACL_TRUE )
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"-- In %s\n", info.memberInfo[info.c_idx]->member );
+ else if ( info.result == ACL_FALSE )
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"-- Not in %s\n", info.memberInfo[info.c_idx]->member );
+
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (info.result == ACL_TRUE) {
+ /*
+ ** that means the client is a member of the
+ ** group or one of the nested groups. We are done.
+ */
+ result = ACL_TRUE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_TRUE\n");
+ goto free_and_return;
+ }
+ numOfMembersVisited++;
+
+ if (numOfMembersVisited == numOfMembersAtCurrentLevel) {
+ /* This means we have looked at all the members for this level */
+ numOfMembersVisited = 0;
+
+ /* Now we are ready to look at the next level */
+ nesting_level++;
+
+ /* So, far we have visited ... */
+ totalMembersVisited += numOfMembersAtCurrentLevel;
+
+ /* How many members in the next level ? */
+ numOfMembersAtCurrentLevel =
+ info.lu_idx - totalMembersVisited +1;
+ }
+
+ if ((nesting_level > max_nestlevel)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "GroupEval:Member not found within the allowed nesting level (Allowed:%d Looked at:%d)\n",
+ max_nestlevel, nesting_level, 0);
+
+ result = ACL_DONT_KNOW; /* don't try to cache info based on this result */
+ goto free_and_return;
+ }
+
+ /* limit of -1 means "no limit */
+ if (info.c_idx > max_memberlimit &&
+ max_memberlimit != -1 ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "GroupEval:Looked at too many entries:(%d, %d)\n",
+ info.c_idx, info.lu_idx,0);
+ result = ACL_DONT_KNOW; /* don't try to cache info based on this result */
+ goto free_and_return;
+ }
+ if (info.lu_idx > info.c_idx) {
+ if (numOfMembers == (info.lu_idx - info.c_idx)) {
+ /* That means it's not a GROUP. It is just another
+ ** useless member which doesn't match. Remove the BAD dude.
+ */
+ groupMember = info.memberInfo[info.c_idx];
+
+ if (groupMember ) {
+ if ( groupMember->member ) slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[info.c_idx] = NULL;
+ }
+ }
+ info.c_idx++;
+
+ /* Go thru the stack and see if we have already
+ ** evaluated this group. If we have, then skip it.
+ */
+ while (1) {
+ int evalNext=0;
+ int j;
+ if (info.c_idx > info.lu_idx) {
+ /* That means we have crossed the limit. We
+ ** may end of in this situation if we
+ ** have circular groups
+ */
+ info.c_idx = info.lu_idx;
+ goto free_and_return;
+ }
+
+ /* Break out of the loop if we have searched to the end */
+ groupMember = info.memberInfo[info.c_idx];
+ if ( (NULL == groupMember) || ((currDN = groupMember->member)!= NULL))
+ break;
+
+ for (j = 0; j < info.c_idx; j++) {
+ groupMember = info.memberInfo[j];
+ if (groupMember->member &&
+ (slapi_utf8casecmp((ACLUCHP)currDN, (ACLUCHP)groupMember->member) == 0)) {
+ /* Don't need the duplicate */
+ groupMember = info.memberInfo[info.c_idx];
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[info.c_idx] = NULL;
+ info.c_idx++;
+ evalNext=1;
+ break;
+ }
+ }
+ if (!evalNext) break;
+ }
+ /* Make sure that we have a valid DN to chug along */
+ groupMember = info.memberInfo[info.c_idx];
+ if ((info.c_idx <= info.lu_idx) && ((currDN = groupMember->member) != NULL))
+ goto eval_another_member;
+ }
+
+free_and_return:
+ /* Remove the unnecessary members from the list which
+ ** we might have accumulated during the last execution
+ ** and we don't need to look at them.
+ */
+ i = info.c_idx;
+ i++;
+ while (i <= info.lu_idx) {
+ groupMember = info.memberInfo[i];
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[i] = NULL;
+ i++;
+ }
+
+ /*
+ ** Now we have a list which has all the groups
+ ** which we need to cache
+ */
+ info.lu_idx = info.c_idx;
+
+ /* since we are updating the groupcache, get a write lock */
+ aclg_lock_groupCache ( 2 /* writer */ );
+
+ /*
+ ** Keep the result of the evaluation in the cache.
+ ** We have 2 lists: member_of and not_member_of. We can use this
+ ** cached information next time we evaluate groups.
+ */
+ if (result == ACL_TRUE &&
+ (cache_status & ACLLAS_CACHE_MEMBER_GROUPS)) {
+ int ngr = 0;
+
+ /* get the last group which the user is a member of */
+ groupMember = info.memberInfo[info.c_idx];
+
+ while ( groupMember ) {
+ int already_cached = 0;
+
+ parentGroup = groupMember->parent;
+ for (j=0; j < u_group->aclug_numof_member_group;j++){
+ if (slapi_utf8casecmp( (ACLUCHP)groupMember->member,
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ already_cached = 1;
+ break;
+ }
+ }
+ if (already_cached) {
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ continue;
+ }
+
+ ngr = u_group->aclug_numof_member_group++;
+ if (u_group->aclug_numof_member_group >=
+ u_group->aclug_member_group_size){
+ u_group->aclug_member_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_member_groups,
+ (u_group->aclug_member_group_size +
+ ACLUG_INCR_GROUPS_LIST) *
+ sizeof (char *));
+ u_group->aclug_member_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_member_groups[ngr] = slapi_ch_strdup ( groupMember->member );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding Group (%s) ParentGroup (%s) to the IN GROUP List\n",
+ groupMember->member , parentGroup ? parentGroup->member: "NULL");
+
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ }
+ } else if (result == ACL_FALSE &&
+ (cache_status & ACLLAS_CACHE_NOT_MEMBER_GROUPS)) {
+ int ngr = 0;
+
+ /* NOT IN THE GROUP LIST */
+ /* get the last group which the user is a member of */
+ groupMember = info.memberInfo[info.c_idx];
+
+ while ( groupMember ) {
+ int already_cached = 0;
+
+ parentGroup = groupMember->parent;
+ for (j=0; j < u_group->aclug_numof_notmember_group;j++){
+ if (slapi_utf8casecmp( (ACLUCHP)groupMember->member,
+ (ACLUCHP)u_group->aclug_notmember_groups[j]) == 0) {
+ already_cached = 1;
+ break;
+ }
+ }
+ if (already_cached) {
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ continue;
+ }
+
+ ngr = u_group->aclug_numof_notmember_group++;
+ if (u_group->aclug_numof_notmember_group >=
+ u_group->aclug_notmember_group_size){
+ u_group->aclug_notmember_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_notmember_groups,
+ (u_group->aclug_notmember_group_size +
+ ACLUG_INCR_GROUPS_LIST) *
+ sizeof (char *));
+ u_group->aclug_notmember_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_notmember_groups[ngr] = slapi_ch_strdup ( groupMember->member );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding Group (%s) ParentGroup (%s) to the NOT IN GROUP List\n",
+ groupMember->member , parentGroup ? parentGroup->member: "NULL");
+
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ }
+ } else if ( result == ACL_DONT_KNOW ) {
+
+ /*
+ * We terminated the search without reaching a conclusion--so
+ * don't cache any info based on this evaluation.
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_DONT_KNOW\n");
+ }
+
+ /* Unlock the group cache, we are done with updating */
+ aclg_unlock_groupCache ( 2 /* writer */ );
+
+ for (i=0; i <= info.lu_idx; i++) {
+ groupMember = info.memberInfo[i];
+ if ( NULL == groupMember ) continue;
+
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ }
+
+ /* free the pointer array.*/
+ slapi_ch_free ( (void **) &info.memberInfo);
+ return result;
+}
+
+/***************************************************************************
+*
+* acllas__handle_group_entry
+*
+* handler called. Compares the userdn value and determines if it's
+* a member of not.
+*
+* Input:
+*
+*
+* Returns:
+*
+* Error Handling:
+*
+**************************************************************************/
+static int
+acllas__handle_group_entry (Slapi_Entry* e, void *callback_data)
+{
+ struct eval_info *info;
+ Slapi_Attr *currAttr, *nextAttr;
+ char *n_dn, *attrType;
+ short n;
+ int i;
+
+ info = (struct eval_info *) callback_data;
+ info->result = ACL_FALSE;
+
+ if (e == NULL) {
+ return 0;
+ }
+
+ slapi_entry_first_attr ( e, &currAttr);
+ if ( NULL == currAttr ) return 0;
+
+ slapi_attr_get_type ( currAttr, &attrType );
+ if (NULL == attrType ) return 0;
+
+ do {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+
+ if ((strcasecmp (attrType, type_member) == 0) ||
+ (strcasecmp (attrType, type_uniquemember) == 0 )) {
+
+ i = slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ struct member_info *groupMember = NULL;
+ attrVal = slapi_value_get_berval ( sval );
+ n_dn = slapi_dn_normalize ( slapi_ch_strdup( attrVal->bv_val));
+ info->lu_idx++;
+ n = info->lu_idx;
+ if (!(n % ACLLAS_MAX_GRP_MEMBER)) {
+ info->memberInfo = (struct member_info **) slapi_ch_realloc(
+ (void *) info->memberInfo,
+ (n+ACLLAS_MAX_GRP_MEMBER) *
+ sizeof(struct eval_info *));
+ }
+
+ /* allocate the space for the member and attch it to the list */
+ groupMember = (struct member_info *) slapi_ch_malloc ( sizeof ( struct member_info ) );
+ groupMember->member = n_dn;
+ groupMember->parent = info->memberInfo[info->c_idx];
+ info->memberInfo[n] = groupMember;
+
+ if (info->userDN &&
+ slapi_utf8casecmp((ACLUCHP)n_dn, (ACLUCHP)info->userDN) == 0) {
+ info->result = ACL_TRUE;
+ return 0;
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ /* Evaluate Dynamic groups */
+ } else if (strcasecmp ( attrType, type_memberURL) == 0) {
+ char *memberURL, *savURL;
+
+ if (!info->userDN) continue;
+
+ i= slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ /*
+ * memberURL may start with "ldap:///" or "ldap://host:port"
+ * ldap://localhost:11000/o=ace industry,c=us??
+ * or
+ * ldap:///o=ace industry,c=us??
+ */
+ if (strncasecmp( attrVal->bv_val, "ldap://",7) == 0 ||
+ strncasecmp( attrVal->bv_val, "ldaps://",8) == 0) {
+ savURL = memberURL = slapi_ch_strdup ( attrVal->bv_val);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL Group Eval:MemberURL:%s\n", memberURL);
+ info->result = acllas__client_match_URL (
+ info->aclpb,
+ info->userDN,
+ memberURL);
+ slapi_ch_free ( (void**) &savURL);
+ if (info->result == ACL_TRUE)
+ return 0;
+ } else {
+ /* This means that the URL is ill-formed */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL Group Eval:Badly Formed MemberURL:%s\n", attrVal->bv_val);
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ /* Evaluate Fortezza groups */
+ } else if ((strcasecmp (attrType, type_memberCert) == 0) ) {
+ /* Do we have the certificate around */
+ if (!info->clientCert) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ " acllas__handle_group_entry:Client Cert missing\n" );
+ continue;
+ }
+ i = slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ if (ldapu_member_certificate_match (
+ info->clientCert,
+ attrVal->bv_val) == LDAP_SUCCESS) {
+ info->result = ACL_TRUE;
+ return 0;
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ }
+
+ attrType = NULL;
+ /* get the next attr */
+ slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( NULL == nextAttr ) break;
+
+ currAttr = nextAttr;
+ slapi_attr_get_type ( currAttr, &attrType );
+
+ } while ( NULL != attrType );
+
+ return 0;
+}
+/***************************************************************************
+*
+* DS_LASGroupDnAttrEval
+*
+*
+* Input:
+* attr_name The string "groupdnattr" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+struct groupdnattr_info
+{
+ char *attrName; /* name of the attribute */
+ int numofGroups; /* number of groups */
+ char **member;
+};
+int
+DS_LASGroupDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *s_attrName = NULL;
+ char *attrName;
+ char *ptr;
+ int matched;
+ int rc;
+ int len;
+ Slapi_Attr *attr;
+ int levels[ACLLAS_MAX_LEVELS];
+ int numOflevels = 0;
+ char *n_currEntryDn = NULL;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_GROUPDNATTR, "DS_LASGroupDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* For anonymous client, the answer is XXX come back to this */
+ if ( lasinfo.anomUser )
+ return LAS_EVAL_FALSE;
+
+ /*
+ ** The groupdnAttr syntax is
+ ** groupdnattr = <attribute>
+ ** Ex:
+ ** groupdnattr = SIEmanager;
+ **
+ ** The function of this LAS is to find out if the client belongs
+ ** to any group that is specified in the attr.
+ */
+ attrName = attr_pattern;
+ if (strstr(attrName, LDAP_URL_prefix)) {
+ char *s;
+
+
+ /* In this case "grppupdnattr="ldap:///base??attr" */
+
+ if ((s = strstr (attrName, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (attrName, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (attrName, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( attrName, &lasinfo,
+ ACL_EVAL_GROUPDNATTR);
+ } else{
+
+ matched = acllas__eval_memberGroupDnAttr(attrName,
+ lasinfo.resourceEntry,
+ lasinfo.clientDn,
+ lasinfo.aclpb);
+ }
+ if ( matched == ACL_DONT_KNOW) {
+ got_undefined = 1;
+ }
+ } else {
+ int i;
+ char *n_groupdn;
+
+ /* ignore leading/trailing whitespace */
+ while(ldap_utf8isspace(attrName)) LDAP_UTF8INC(attrName);
+ len = strlen(attrName);
+ ptr = attrName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"Attr:%s\n" , attrName, 0,0);
+
+ /* See if we have a parent[2].attr" rule */
+ if ( (ptr = strstr(attrName, "parent[")) != NULL) {
+ char *word, *str, *next;
+
+ numOflevels = 0;
+ n_currEntryDn = slapi_entry_get_ndn ( lasinfo.resourceEntry ) ;
+ s_attrName = attrName = slapi_ch_strdup ( attr_pattern );
+ str = attrName;
+
+ word = ldap_utf8strtok_r(str, "[],. ",&next);
+ /* The first word is "parent[" and so it's not important */
+
+ while ((word= ldap_utf8strtok_r(NULL, "[],.", &next)) != NULL) {
+ if (ldap_utf8isdigit(word)) {
+ while (word && ldap_utf8isspace(word)) LDAP_UTF8INC(word);
+ if (numOflevels < ACLLAS_MAX_LEVELS)
+ levels[numOflevels++] = atoi (word);
+ else {
+ /*
+ * Here, ignore the extra levels..it's really
+ * a syntax error which should have been ruled out at parse time
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASGroupDnattr: Exceeded the ATTR LIMIT:%d: Ignoring extra levels\n",
+ ACLLAS_MAX_LEVELS,0,0);
+ }
+ } else {
+ /* Must be the attr name. We can goof of by
+ ** having parent[1,2,a] but then you have to be
+ ** stupid to do that.
+ */
+ char *p = word;
+ if (*--p == '.') {
+ attrName = word;
+ break;
+ }
+ }
+ }
+ } else {
+ levels[0] = 0;
+ numOflevels = 1;
+ }
+
+ matched = ACL_FALSE;
+ for (i=0; i < numOflevels; i++) {
+ if ( levels[i] == 0 ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int attr_i;
+
+ /*
+ * For the add operation, the resource itself (level 0)
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ * XXX is this therefore FALSE or DONT_KNOW ?
+ */
+
+ if ( lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: groupdnAttr does not allow ADD permission at level 0.\n");
+ got_undefined = 1;
+ continue;
+ }
+ slapi_entry_attr_find ( lasinfo.resourceEntry, attrName, &attr);
+ if ( !attr) continue;
+ attr_i= slapi_attr_first_value ( attr,&sval );
+ while ( attr_i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ n_groupdn = slapi_dn_normalize(
+ slapi_ch_strdup( attrVal->bv_val));
+ matched = acllas__user_ismember_of_group (
+ lasinfo.aclpb, n_groupdn, lasinfo.clientDn,
+ ACLLAS_CACHE_MEMBER_GROUPS,
+ lasinfo.aclpb->aclpb_clientcert);
+ slapi_ch_free ( (void **) &n_groupdn);
+ if (matched == ACL_TRUE ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "groupdnattr matches at level (%d)\n", levels[i]);
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ attr_i= slapi_attr_next_value ( attr, attr_i, &sval );
+ }
+ } else {
+ char *p_dn;
+ struct groupdnattr_info info;
+ char *attrs[2];
+ int j;
+
+ info.numofGroups = 0;
+ attrs[0] = info.attrName = attrName;
+ attrs[1] = NULL;
+
+ p_dn = acllas__dn_parent (n_currEntryDn, levels[i]);
+
+ if (p_dn == NULL) continue;
+
+ /* Use new search internal API */
+ {
+
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--if the user's definition is
+ * remote and the group is dynamic and the user entry
+ * changes then we would not notice--so don't go
+ * find the user entry in the first place.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ p_dn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__get_members,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (info.numofGroups <= 0) {
+ continue;
+ }
+ for (j=0; j <info.numofGroups; j++) {
+ if (slapi_utf8casecmp((ACLUCHP)info.member[j],
+ (ACLUCHP)lasinfo.clientDn) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ matched = acllas__user_ismember_of_group (
+ lasinfo.aclpb, info.member[j],
+ lasinfo.clientDn, ACLLAS_CACHE_ALL_GROUPS,
+ lasinfo.aclpb->aclpb_clientcert);
+ if (matched == ACL_TRUE) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Deallocate the member array and the member struct */
+ for (j=0; j < info.numofGroups; j++)
+ slapi_ch_free ((void **) &info.member[j]);
+ slapi_ch_free ((void **) &info.member);
+ }
+ if (matched == ACL_TRUE) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "groupdnattr matches at level (%d)\n", levels[i]);
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group at another level
+ * will evaluate to TRUE.
+ */
+ got_undefined = 1;
+ }
+
+ } /* NumofLevels */
+ }
+ if (s_attrName) slapi_ch_free ((void**) &s_attrName );
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for groupdnattr evaluation.\n");
+ }
+
+ return rc;
+}
+
+/*
+ * acllas__eval_memberGroupDnAttr
+ *
+ * return ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW
+ *
+ * Inverse group evaluation. Find all the groups that the user is a
+ * member of. Find all teh groups that contain those groups. Do an
+ * upward nested level search. By the end of it, we will know all the
+ * groups that the clinet is a member of under that search scope.
+ *
+ * This model seems to be very fast if we have few groups at the
+ * leaf level.
+ *
+ */
+static int
+acllas__eval_memberGroupDnAttr (char *attrName, Slapi_Entry *e,
+ char *n_clientdn, struct acl_pblock *aclpb)
+{
+
+ Slapi_Attr *attr;
+ char *s, *p;
+ char *str, *s_str, *base, *groupattr;
+ int i,j,k,matched, enumerate_groups;
+ aclUserGroup *u_group;
+ char ebuf [ BUFSIZ ];
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+
+ /* Parse the URL -- We can't use the ldap_url_parse()
+ ** we don't follow thw complete url naming scheme
+ */
+ s_str = str = slapi_ch_strdup(attrName);
+ while (str && ldap_utf8isspace(str)) LDAP_UTF8INC( str );
+ str +=8;
+ s = strchr (str, '?');
+ if (s) {
+ p = s;
+ p++;
+ *s = '\0';
+ base = str;
+ s = strchr (p, '?');
+ if (s) *s = '\0';
+
+ groupattr = p;
+ } else {
+ slapi_ch_free ( (void **)&s_str );
+ return ACL_FALSE;
+ }
+
+ if ( (u_group = aclg_get_usersGroup ( aclpb , n_clientdn )) == NULL) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Failed to find/allocate a usergroup--aborting evaluation\n", 0, 0);
+ slapi_ch_free ( (void **)&s_str );
+ return(ACL_DONT_KNOW);
+ }
+
+ /*
+ ** First find out if we have already searched this base or
+ ** if we are searching a subtree to an already enumerated base.
+ */
+ enumerate_groups = 1;
+ for (j=0; j < aclpb->aclpb_numof_bases; j++) {
+ if (slapi_dn_issuffix(aclpb->aclpb_grpsearchbase[j], base)) {
+ enumerate_groups = 0;
+ break;
+ }
+ }
+
+
+ /* See if we have already enumerated all the groups which the
+ ** client is a member of.
+ */
+ if (enumerate_groups) {
+ char filter_str[BUFSIZ];
+ char *attrs[3];
+ struct eval_info info;
+ char *curMemberDn;
+ int Done = 0;
+ int ngr, tt;
+
+ /* Add the scope to the list of scopes */
+ if (aclpb->aclpb_numof_bases >= (aclpb->aclpb_grpsearchbase_size-1)) {
+ aclpb->aclpb_grpsearchbase = (char **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_grpsearchbase,
+ (aclpb->aclpb_grpsearchbase_size +
+ ACLPB_INCR_BASES) *
+ sizeof (char *));
+ aclpb->aclpb_grpsearchbase_size += ACLPB_INCR_BASES;
+ }
+ aclpb->aclpb_grpsearchbase[aclpb->aclpb_numof_bases++] =
+ slapi_dn_normalize(slapi_ch_strdup(base));
+
+ /* Set up info to do a search */
+ attrs[0] = type_member;
+ attrs[1] = type_uniquemember;
+ attrs[2] = NULL;
+
+ info.c_idx = info.lu_idx = 0;
+ info.member =
+ (char **) slapi_ch_malloc (ACLLAS_MAX_GRP_MEMBER * sizeof(char *));
+ curMemberDn = n_clientdn;
+
+ while (!Done) {
+ char *filter_str_ptr = &filter_str[0];
+ char *new_filter_str = NULL;
+ int lenf = strlen(curMemberDn)<<1;
+
+ if (lenf > (BUFSIZ - 28)) { /* 28 for "(|(uniquemember=%s)(member=%s))" */
+ new_filter_str = slapi_ch_malloc(lenf + 28);
+ filter_str_ptr = new_filter_str;
+ }
+
+ /*
+ ** Search the db for groups that the client is a member of.
+ ** Once found cache it. cache only unique groups.
+ */
+ tt = info.lu_idx;
+ sprintf (filter_str_ptr,"(|(uniquemember=%s)(member=%s))",
+ curMemberDn, curMemberDn);
+
+ /* Use new search internal API */
+ {
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--we demand that group
+ * definition be local.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ base,
+ LDAP_SCOPE_SUBTREE,
+ filter_str_ptr,
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__add_allgroups,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (new_filter_str) slapi_ch_free((void **) &new_filter_str);
+
+ if (tt == info.lu_idx) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "currDn:(%s) \n\tNO MEMBER ADDED\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (curMemberDn, ebuf) , 0,0);
+ } else {
+ for (i=tt; i < info.lu_idx; i++)
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "currDn:(%s) \n\tADDED MEMBER[%d]=%s\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (curMemberDn, ebuf), i, info.member[i]);
+ }
+
+ if (info.c_idx >= info.lu_idx) {
+ for (i=0; i < info.lu_idx; i++) {
+ int already_cached = 0;
+ for (j=0; j < u_group->aclug_numof_member_group;
+ j++){
+ if (slapi_utf8casecmp(
+ (ACLUCHP)info.member[i],
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ slapi_ch_free ((void **) &info.member[i] );
+ info.member[i] = NULL;
+ already_cached = 1;
+ break;
+ }
+
+ }
+
+ if (already_cached) continue;
+
+ ngr = u_group->aclug_numof_member_group++;
+ if (u_group->aclug_numof_member_group >=
+ u_group->aclug_member_group_size){
+ u_group->aclug_member_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_member_groups,
+ (u_group->aclug_member_group_size +
+ ACLUG_INCR_GROUPS_LIST) * sizeof(char *));
+
+ u_group->aclug_member_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_member_groups[ngr] = info.member[i];
+ info.member[i] = NULL;
+ }
+ slapi_ch_free ((void **) &info.member);
+ Done = 1;
+ } else {
+ curMemberDn = info.member[info.c_idx];
+ info.c_idx++;
+ }
+ }
+ }
+
+ for (j=0; j < u_group->aclug_numof_member_group; j++)
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "acllas__eval_memberGroupDnAttr:GROUP[%d] IN CACHE:%s\n",
+ j, ACL_ESCAPE_STRING_WITH_PUNCTUATION (u_group->aclug_member_groups[j], ebuf),0);
+
+ matched = ACL_FALSE;
+ slapi_entry_attr_find( e, groupattr, &attr);
+ if (attr == NULL) {
+ slapi_ch_free ( (void **)&s_str );
+ return ACL_FALSE;
+ }
+ {
+ k = slapi_attr_first_value ( attr,&sval );
+ while ( k != -1 ) {
+ char *n_attrval;
+ attrVal = slapi_value_get_berval ( sval );
+ n_attrval = slapi_ch_strdup( attrVal->bv_val);
+ n_attrval = slapi_dn_normalize (n_attrval);
+
+ /* We support: The attribute value can be a USER or a GROUP.
+ ** Let's compare with the client, thi might be just an user. If it is not
+ ** then we test it against the list of groups.
+ */
+ if (slapi_utf8casecmp ((ACLUCHP)n_attrval, (ACLUCHP)n_clientdn) == 0 ) {
+ matched = ACL_TRUE;
+ slapi_ch_free ( (void **)&n_attrval );
+ break;
+ }
+ for (j=0; j <u_group->aclug_numof_member_group; j++) {
+ if ( slapi_utf8casecmp((ACLUCHP)n_attrval,
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ }
+ slapi_ch_free ( (void **)&n_attrval );
+ if (matched == ACL_TRUE) break;
+ k= slapi_attr_next_value ( attr, k, &sval );
+ }
+ }
+ slapi_ch_free ( (void **)&s_str );
+ return matched;
+}
+
+static int
+acllas__add_allgroups (Slapi_Entry* e, void *callback_data)
+{
+ int i, n, m;
+ struct eval_info *info;
+ char *n_dn;
+
+ info = (struct eval_info *) callback_data;
+
+ /*
+ ** Once we are here means this is a valid group. First see
+ ** If we have already seen this group. If not, add it to the
+ ** member list.
+ */
+ n_dn = slapi_ch_strdup ( slapi_entry_get_ndn ( e ) );
+ for (i=0; i < info->lu_idx; i++) {
+ if (slapi_utf8casecmp((ACLUCHP)n_dn, (ACLUCHP)info->member[i]) == 0) {
+ slapi_ch_free ( (void **) &n_dn);
+ return 0;
+ }
+ }
+
+ m = info->lu_idx;
+ n = info->lu_idx++;
+ if (!(n % ACLLAS_MAX_GRP_MEMBER)) {
+ info->member = (char **) slapi_ch_realloc (
+ (void *) info->member,
+ (n+ACLLAS_MAX_GRP_MEMBER) * sizeof(char *));
+ }
+ info->member[m] = n_dn;
+ return 0;
+}
+/*
+ *
+ * acllas__dn_parent
+ *
+ * This code should belong to dn.c. However this is specific to acl and I had
+ * 2 choices 1) create a new API or 2) reuse the slapi_dN_parent
+ *
+ * Returns a ptr to the parent based on the level.
+ *
+ */
+#define DNSEPARATOR(c) (c == ',' || c == ';')
+static char*
+acllas__dn_parent( char *dn, int level)
+{
+ char *s, *dnstr;
+ int inquote;
+ int curLevel;
+ int lastLoop = 0;
+
+ if ( dn == NULL || *dn == '\0' ) {
+ return( NULL );
+ }
+
+ /* An X.500-style name, which looks like foo=bar,sha=baz,... */
+ /* Do we have any dn seprator or not */
+ if ((strchr(dn,',') == NULL) && (strchr(dn,';') == NULL))
+ return (NULL);
+
+ inquote = 0;
+ curLevel = 1;
+ dnstr = dn;
+ while ( curLevel <= level) {
+ if (lastLoop) break;
+ if (curLevel == level) lastLoop = 1;
+ for ( s = dnstr; *s; s++ ) {
+ if ( *s == '\\' ) {
+ if ( *(s + 1) )
+ s++;
+ continue;
+ }
+ if ( inquote ) {
+ if ( *s == '"' )
+ inquote = 0;
+ } else {
+ if ( *s == '"' )
+ inquote = 1;
+ else if ( DNSEPARATOR( *s ) ) {
+ if (curLevel == level)
+ return( s + 1 );
+ dnstr = s + 1;
+ curLevel++;
+ break;
+ }
+ }
+ }
+ if ( *s == '\0') {
+ /* Got to the end of the string without reaching level,
+ * so return NULL.
+ */
+ return(NULL);
+ }
+ }
+
+ return( NULL );
+}
+/*
+ * acllas__verify_client
+ *
+ * returns 1 if the attribute exists in the entry and
+ * it's value is equal to the client Dn.
+ * If the attribute is not in the entry, or it is and the
+ * value differs from the clientDn then returns FALSE.
+ *
+ * Verify if client's DN is stored in the attrbute or not.
+ * This is a handler from a search being done at
+ * DS_LASUserDnAttrEval().
+ *
+ */
+static int
+acllas__verify_client (Slapi_Entry* e, void *callback_data)
+{
+
+ Slapi_Attr *attr;
+ char *val;
+ struct userdnattr_info *info;
+ Slapi_Value *sval;
+ const struct berval *attrVal;
+ int i;
+
+ info = (struct userdnattr_info *) callback_data;
+
+ slapi_entry_attr_find( e, info->attr, &attr);
+ if (attr == NULL) return 0;
+
+ i = slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ val = slapi_dn_normalize (
+ slapi_ch_strdup(attrVal->bv_val));
+
+ if (slapi_utf8casecmp((ACLUCHP)val, (ACLUCHP)info->clientdn ) == 0) {
+ info->result = 1;
+ slapi_ch_free ( (void **) &val);
+ return 0;
+ }
+ slapi_ch_free ( (void **) &val);
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ return 0;
+}
+/*
+ *
+ * acllas__get_members
+ *
+ * Collects all the values of the specified attribute which should be group names.
+ */
+static int
+acllas__get_members (Slapi_Entry* e, void *callback_data)
+{
+
+ Slapi_Attr *attr;
+ struct groupdnattr_info *info;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ info = (struct groupdnattr_info *) callback_data;
+ slapi_entry_attr_find (e, info->attrName, &attr);
+ if ( !attr ) return 0;
+
+ slapi_attr_get_numvalues ( attr, &info->numofGroups );
+
+ info->member = (char **) slapi_ch_malloc (info->numofGroups * sizeof(char *));
+ i = slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal =slapi_value_get_berval ( sval );
+ info->member[i] = slapi_dn_normalize ( slapi_ch_strdup(attrVal->bv_val));
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ return 0;
+}
+
+/*
+ * DS_LASUserAttrEval
+ * LAS to evaluate the userattr rule
+ *
+ * userAttr = "attrName#Type"
+ *
+ * <Type> ::= "USERDN" | "GROUPDN" | "ROLEDN" | "LDAPURL" | <value>
+ * <value>::== <any printable String>
+ *
+ * Example:
+ * userAttr = "manager#USERDN" --- same as userdnattr
+ * userAttr = "owner#GROUPDN" --- same as groupdnattr
+ * = "ldap:///o=sun.com?owner#GROUPDN
+ * userAttr = "attr#ROLEDN" --- The value of attr contains a roledn
+ * userAttr = "myattr#LDAPURL" --- The value contains a LDAP URL
+ * which can have scope and filter
+ * bits.
+ * userAttr = "OU#Directory Server"
+ * --- In this case the client's OU and the
+ * resource entry's OU must have
+ * "Directory Server" value.
+ *
+ * Returns:
+ * retcode The usual LAS return codes.
+ */
+int
+DS_LASUserAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *attrName;
+ char *attrValue = NULL;
+ int rc;
+ int matched = ACL_FALSE;
+ char *p;
+ int URLAttrRule = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERATTR, "DS_LASUserAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Which rule are we evaluating ? */
+ attrName = slapi_ch_strdup (attr_pattern );
+ if ( NULL == (p = strchr ( attrName, '#' ))) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval:Invalid value(%s)\n", attr_pattern);
+ slapi_ch_free ( (void **) &attrName );
+ return LAS_EVAL_FAIL;
+ }
+ attrValue = p;
+ attrValue++; /* skip the # */
+ *p = '\0'; /* null terminate the attr name */
+
+ if ( 0 == strncasecmp ( attrValue, "USERDN", 6)) {
+ matched = DS_LASUserDnAttrEval (errp,DS_LAS_USERDNATTR, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ } else if ( 0 == strncasecmp ( attrValue, "GROUPDN", 7)) {
+ matched = DS_LASGroupDnAttrEval (errp,DS_LAS_GROUPDNATTR, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ } else if ( 0 == strncasecmp ( attrValue, "LDAPURL", 7) ) {
+ URLAttrRule = 1;
+ } else if ( 0 == strncasecmp ( attrValue, "ROLEDN", 6)) {
+ matched = DS_LASRoleDnAttrEval (errp,DS_LAS_ROLEDN, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ }
+
+ if ( lasinfo.aclpb && ( NULL == lasinfo.aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 pass NULL in case the req is chained */
+ char **attrs=NULL;
+
+
+ /* Use new search internal API */
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ lasinfo.clientDn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ lasinfo.aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+
+ }
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval: AttrName:%s, attrVal:%s\n", attrName, attrValue );
+
+ if ( URLAttrRule ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ Slapi_Attr *attrs;
+ int i;
+
+ /* Get the attr from the resouce entry */
+ if ( 0 == slapi_entry_attr_find (lasinfo.resourceEntry, attrName, &attrs) ) {
+ i= slapi_attr_first_value ( attrs, &sval );
+ if ( i==-1 ) {
+ matched = ACL_FALSE; /* Attr val not there so it's value cannot equal other one */
+ goto done_acl;
+ }
+ } else {
+ matched = ACL_FALSE; /* Not there so it cannot equal another one */
+ goto done_acl;
+ }
+
+ while( matched != ACL_TRUE && (sval != NULL)) {
+ attrVal = slapi_value_get_berval ( sval );
+ matched = acllas__client_match_URL ( lasinfo.aclpb,
+ lasinfo.clientDn,
+ attrVal->bv_val);
+ if ( matched != ACL_TRUE )
+ i = slapi_attr_next_value ( attrs, i, &sval );
+ if ( matched == ACL_DONT_KNOW ) {
+ got_undefined = 1;
+ }
+ }/* while */
+ } else {
+ /*
+ * Here it's the userAttr = "OU#Directory Server" case.
+ * Allocate the Slapi_Value on the stack and init it by reference
+ * to avoid having to malloc and free memory.
+ */
+ Slapi_Value v;
+
+ slapi_value_init_string_passin(&v, attrValue);
+ rc = slapi_entry_attr_has_syntax_value ( lasinfo.resourceEntry, attrName,
+ &v );
+ if (rc) {
+ rc = slapi_entry_attr_has_syntax_value (
+ lasinfo.aclpb->aclpb_client_entry,
+ attrName, &v );
+ if (rc) matched = ACL_TRUE;
+ }
+ /* Nothing to free--cool */
+ }
+
+ /*
+ * Find out what the result is, in
+ * this case matched is one of ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW
+ * and got_undefined says whether a logical term evaluated to ACL_DONT_KNOW.
+ *
+ */
+done_acl:
+ if ( matched == ACL_TRUE || !got_undefined) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ }
+
+ slapi_ch_free ( (void **) &attrName );
+ return rc;
+
+done_las:
+ /*
+ * In this case matched is already LAS_EVAL_TRUE or LAS_EVAL_FALSE or
+ * LAS_EVAL_FAIL.
+ */
+ if ( matched != LAS_EVAL_FAIL ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = matched;
+ } else {
+ rc = (matched == LAS_EVAL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ }
+
+ slapi_ch_free ( (void **) &attrName );
+ return rc;
+}
+
+/*
+ * acllas__client_match_URL
+ * Match a client to a URL.
+ *
+ * Returns:
+ * ACL_TRUE - matched the URL
+ * ACL_FALSE - Sorry; no match
+ *
+ */
+static int
+acllas__client_match_URL (struct acl_pblock *aclpb, char *n_clientdn, char *url )
+{
+
+ LDAPURLDesc *ludp;
+ int rc;
+ Slapi_Filter *f = NULL;
+
+
+ /* Get the client's entry if we don't have already */
+ if ( aclpb && ( NULL == aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 Get every attr in case req chained */
+ char **attrs=NULL;
+
+ /* Use new search internal API */
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ n_clientdn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if ( NULL == aclpb->aclpb_client_entry ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval: Unable to get client's entry\n",0,0,0);
+ return ACL_FALSE;
+ }
+
+ if (( rc = ldap_url_parse( url, &ludp)) != 0 ) {
+ return ACL_FALSE;
+
+ }
+ if ( ( NULL == ludp->lud_dn) || ( NULL == ludp->lud_filter) ) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+
+ /* Normalize in place the dn */
+ slapi_dn_normalize ( ludp->lud_dn );
+
+ /* Check the scope */
+ if ( ludp->lud_scope == LDAP_SCOPE_SUBTREE ) {
+ if (!slapi_dn_issuffix(n_clientdn, ludp->lud_dn)) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+ } else if ( ludp->lud_scope == LDAP_SCOPE_ONELEVEL ) {
+ char *parent = slapi_dn_parent (n_clientdn);
+
+ if (slapi_utf8casecmp ((ACLUCHP)parent, (ACLUCHP)ludp->lud_dn) != 0 ) {
+ slapi_ch_free ( (void **) &parent);
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+ slapi_ch_free ( (void **) &parent);
+ } else { /* default */
+ if (slapi_utf8casecmp ( (ACLUCHP)n_clientdn, (ACLUCHP)ludp->lud_dn) != 0 ) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+
+ }
+
+
+ /* Convert the filter string */
+ f = slapi_str2filter ( ludp->lud_filter );
+
+ rc = ACL_TRUE;
+ if (0 != slapi_vattr_filter_test ( aclpb->aclpb_pblock,
+ aclpb->aclpb_client_entry, f, 0 /* no acces chk */ ))
+ rc = ACL_FALSE;
+
+ ldap_free_urldesc( ludp );
+ slapi_filter_free ( f, 1 ) ;
+
+ return rc;
+}
+static int
+acllas__handle_client_search ( Slapi_Entry *e, void *callback_data )
+{
+ struct acl_pblock *aclpb = (struct acl_pblock *) callback_data;
+
+ /* If we are here means we have found the entry */
+ if ( NULL == aclpb-> aclpb_client_entry)
+ aclpb->aclpb_client_entry = slapi_entry_dup ( e );
+ return 0;
+}
+/*
+*
+* Do all the necessary setup for all the
+* LASes.
+* It will only fail if it's passed garbage (which should not happen) or
+* if the data it needs to stock the lasinfo is not available, which
+* also should not happen.
+*
+*
+* Return value: 0 or one of these
+* #define LAS_EVAL_TRUE -1
+* #define LAS_EVAL_FALSE -2
+* #define LAS_EVAL_DECLINE -3
+* #define LAS_EVAL_FAIL -4
+* #define LAS_EVAL_INVALID -5
+*/
+
+static int
+__acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth, char *lasType, char*lasName, lasInfo *linfo)
+{
+
+ int rc;
+ memset ( linfo, 0, sizeof ( lasInfo) );
+
+ *cachable = 0;
+ *LAS_cookie = (void *)0;
+
+ if (strcmp(attr_name, lasType) != 0) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Invalid LAS(%s)\n", lasName, attr_name);
+ return LAS_EVAL_INVALID;
+ }
+
+ if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Invalid comparator(%d)\n", lasName, (int)comparator);
+ return LAS_EVAL_INVALID;
+ }
+
+
+ /* Get the client DN */
+ rc = ACL_GetAttribute(errp, DS_ATTR_USERDN, (void **)&linfo->clientDn,
+ subject, resource, auth_info, global_auth);
+
+ if ( rc != LAS_EVAL_TRUE ) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the clientdn attribute(%d)\n",lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Check if we have a user or not */
+ if (linfo->clientDn) {
+ /* See if it's a anonymous user */
+ if (*(linfo->clientDn) == '\0')
+ linfo->anomUser = ACL_TRUE;
+ } else {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s: No user\n",lasName);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ((rc = PListFindValue(subject, DS_ATTR_ENTRY,
+ (void **)&linfo->resourceEntry, NULL)) < 0) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the Slapi_Entry attr(%d)\n",lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Get ACLPB */
+ rc = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&linfo->aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rc != LAS_EVAL_TRUE ) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the ACLPB(%d)\n", lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+ if (NULL == attr_pattern ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:No rule value in the ACL\n", lasName);
+
+ return LAS_EVAL_FAIL;
+ }
+ /* get the authentication type */
+ if ((rc = PListFindValue(subject, DS_ATTR_AUTHTYPE,
+ (void **)&linfo->authType, NULL)) < 0) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the auth type(%d)\n", rc);
+ return LAS_EVAL_FAIL;
+ }
+ return 0;
+}
+
+/*
+ * See if clientDN has role roleDN.
+ * Here we know the user is not anon and that the role
+ * is not the anyone role ie. it's actually worth invoking the roles code.
+*/
+
+static int acllas__user_has_role( struct acl_pblock *aclpb,
+ Slapi_DN *roleDN, Slapi_DN *clientDn) {
+
+ int present = 0;
+ int rc = 0;
+
+ /* Get the client's entry if we don't have already */
+ if ( aclpb && ( NULL == aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 Get every attr in case req chained */
+ char **attrs=NULL;
+
+ /* Use new search internal API */
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--the user and the role definition
+ * must be co-located (chaining is not supported for the roles
+ * plugin in 5.0
+ */
+ slapi_search_internal_set_pb ( aPb,
+ slapi_sdn_get_ndn(clientDn),
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if ( NULL == aclpb->aclpb_client_entry ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acllas__user_has_role: Unable to get client's entry\n",0,0,0);
+ return ACL_FALSE;
+ }
+
+ /* If the client has the role then it's a match, otherwise no */
+
+ rc = slapi_role_check( aclpb->aclpb_client_entry, roleDN, &present);
+ if ( present ) {
+ return(ACL_TRUE);
+ }
+
+ return(ACL_FALSE);
+}
+
+int
+DS_LASRoleDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *s_attrName = NULL;
+ char *attrName;
+ int matched;
+ int rc;
+ Slapi_Attr *attr;
+ int numOflevels = 0;
+ char *n_currEntryDn = NULL;
+ lasInfo lasinfo;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int k=0;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_ROLEDN, "DS_LASRoleDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* For anonymous client, they have no roles so the match is false. */
+ if ( lasinfo.anomUser )
+ return LAS_EVAL_FALSE;
+
+ /*
+ **
+ ** The function of this LAS is to find out if the client has
+ ** the role specified in the attr.
+ ** attr_pattern looks like: "ROLEDN cn=role1,o=sun.com"
+ */
+ attrName = attr_pattern;
+
+ matched = ACL_FALSE;
+ slapi_entry_attr_find( lasinfo.resourceEntry, attrName, &attr);
+ if (attr == NULL) {
+ /*
+ * Here the entry does not contain the attribute so the user
+ * cannot have this "null" role
+ */
+ return LAS_EVAL_FALSE;
+ }
+
+ if (lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ /*
+ * Here the entry does not contain the attribute so the user
+ * cannot have this "null" role or
+ * For the add operation, the resource itself
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ * XXX is this therefore FALSE or DONT_KNOW ?
+ *
+ *
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: userattr=XXX#ROLEDN does not allow ADD permission.\n");
+ got_undefined = 1;
+ } else {
+
+ /*
+ * Got the first value.
+ * Test all the values of this attribute--if the client has _any_
+ * of the roles then it's a match.
+ */
+ k = slapi_attr_first_value ( attr,&sval );
+ while ( k != -1 ) {
+ char *n_attrval;
+ Slapi_DN *roleDN;
+
+ attrVal = slapi_value_get_berval ( sval );
+ n_attrval = slapi_ch_strdup( attrVal->bv_val);
+ n_attrval = slapi_dn_normalize (n_attrval);
+ roleDN = slapi_sdn_new_dn_byval(n_attrval);
+
+ /* We support: The attribute value can be a USER or a GROUP.
+ ** Let's compare with the client, thi might be just an user. If it is not
+ ** then we test it against the list of groups.
+ */
+ if ((matched = acllas__user_has_role(
+ lasinfo.aclpb,
+ roleDN,
+ lasinfo.aclpb->aclpb_authorization_sdn)) == ACL_TRUE){
+ slapi_ch_free ( (void **)&n_attrval );
+ slapi_sdn_free(&roleDN );
+ break;
+ }
+ slapi_ch_free ( (void **)&n_attrval );
+ slapi_sdn_free(&roleDN );
+ if (matched == ACL_TRUE) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ k= slapi_attr_next_value ( attr, k, &sval );
+ }/* while */
+ }
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ }
+ return (rc);
+}
+
+/*
+ * Here, determine if lasinfo->clientDn matches user (which contains
+ * a ($dn) or a $attr component or both.) As defined in the aci
+ * lasinfo->aclpb->aclpb_curr_aci,
+ * which is the current aci being evaluated.
+ *
+ * returns: ACL_TRUE for matched,
+ * ACL_FALSE for matched.
+ * ACL_DONT_KNOW otherwise.
+ *
+ *
+*/
+
+int
+aclutil_evaluate_macro( char * rule, lasInfo *lasinfo,
+ acl_eval_types evalType ) {
+
+ int matched = 0;
+ aci_t *aci;
+ char *matched_val = NULL;
+ char **candidate_list = NULL;
+ char **inner_list = NULL;
+ char **sptr = NULL;
+ char **tptr = NULL;
+ char *t = NULL;
+ char *s = NULL;
+ char *target_dn = NULL;
+ struct acl_pblock *aclpb = lasinfo->aclpb;
+ int found_matched_val_in_ht = 0;
+
+ aci = lasinfo->aclpb->aclpb_curr_aci;
+ /* Get a pointer to the ndn in the resouirce */
+ target_dn = slapi_entry_get_ndn ( lasinfo->resourceEntry );
+
+ /*
+ * First, get the matched value from the target resource.
+ * We have alredy done this matching once beofer at tasrget match time.
+ */
+
+ LDAPDebug ( LDAP_DEBUG_ACL, "aclutil_evaluate_macro for aci '%s'"
+ "index '%d'\n",
+ aci->aclName, aci->aci_index,0);
+
+ if ( aci->aci_macro == NULL ) {
+ /* No $dn in the target, it's a $attr type subject rule */
+ matched_val = NULL;
+ } else {
+
+ /*
+ * Look up the matched_val value calculated
+ * from the target and stored judiciously there for us.
+ */
+
+ if ( (matched_val = (char *)acl_ht_lookup( aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index)) == NULL) {
+ LDAPDebug( LDAP_DEBUG_ACL,
+ "ACL info: failed to locate the calculated target"
+ "macro for aci '%s' index '%d'\n",
+ aci->aclName, aci->aci_index,0);
+ return(ACL_FALSE); /* Not a match */
+ } else {
+ LDAPDebug( LDAP_DEBUG_ACL,
+ "ACL info: found matched_val (%s) for aci index %d"
+ "in macro ht\n",
+ aci->aclName, aci->aci_index,0);
+
+ found_matched_val_in_ht = 1;
+ }
+ }
+
+ /*
+ * Now, make a candidate
+ * list of strings to match against the client.
+ * This involves replacing ($dn) or [$dn] by either the matched
+ * value, or all the suffix substrings of matched_val.
+ * If there is no $dn then the candidate list is just
+ * user itself.
+ *
+ */
+
+ candidate_list = acllas_replace_dn_macro( rule, matched_val, lasinfo);
+
+ sptr= candidate_list;
+ while( *sptr != NULL && !matched) {
+
+ s = *sptr;
+
+ /*
+ * Now s may contain some $attr macros.
+ * So, make a candidate list, got by replacing each occurence
+ * of $attr with all the values that attribute has in
+ * the resource entry.
+ */
+
+ inner_list = acllas_replace_attr_macro( s, lasinfo);
+
+ tptr = inner_list;
+ while( *tptr != NULL && (matched != ACL_TRUE) ){
+
+ t = *tptr;
+
+ /*
+ * Now, at last t is a candidate string we can
+ * match agains the client.
+ *
+ * $dn and $attr can appear in userdn, graoupdn and roledn
+ * rules, so we we need to decide which type we
+ * currently evaluating and evaluate that.
+ *
+ * If the string generated was undefined, eg it contained
+ * ($attr.ou) and the entry did not have an ou attribute,then
+ * the empty string is returned for this. So it we find
+ * an empty string in the list, skip it--it does not match.
+ */
+
+ if ( *t != '\0') {
+ if ( evalType == ACL_EVAL_USER ) {
+
+ matched = acllas_eval_one_user( lasinfo->aclpb,
+ lasinfo->clientDn, t);
+ } else if (evalType == ACL_EVAL_GROUP) {
+
+ matched = acllas_eval_one_group(t, lasinfo);
+ } else if (evalType == ACL_EVAL_ROLE) {
+ matched = acllas_eval_one_role(t, lasinfo);
+ } else if (evalType == ACL_EVAL_GROUPDNATTR) {
+ matched = acllas__eval_memberGroupDnAttr(t,
+ lasinfo->resourceEntry,
+ lasinfo->clientDn,
+ lasinfo->aclpb);
+ } else if ( evalType == ACL_EVAL_TARGET_FILTER) {
+
+ matched = acllas_eval_one_target_filter(t,
+ lasinfo->resourceEntry);
+
+ }
+ }
+
+ tptr++;
+
+ }/*inner while*/
+ charray_free(inner_list);
+
+ sptr++;
+ }/* outer while */
+
+ charray_free(candidate_list);
+
+ return(matched);
+
+}
+
+/*
+ * Here, replace the first occurrence of $(dn) with matched_val.
+ * replace any occurrence of $[dn] with each of the suffix substrings
+ * of matched_val.
+ * Each of these strings is returned in a NULL terminated list of strings.
+ *
+ * If there is no $dn thing then the returned list just contains rule itself.
+ *
+ * eg. rule: cn=fred,ou=*, ($dn), o=sun.com
+ * matched_val: ou=People,o=icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=icnc,o=sun.com NULL
+ *
+ * eg. rule: cn=fred,ou=*,[$dn], o=sun.com
+ * matched_val: ou=People,o=icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=icnc,o=sun.com
+ * cn=fred,ou=*,o=icnc,o=sun.com
+ * NULL
+ *
+ *
+*/
+
+static char **
+acllas_replace_dn_macro( char *rule, char *matched_val, lasInfo *lasinfo) {
+
+ char **a = NULL;
+ char *str = NULL;
+ char *patched_rule = NULL;
+ char *rule_to_use = NULL;
+ char *new_patched_rule = NULL;
+ char *rule_prefix = NULL;
+ char *rule_suffix = NULL;
+ int rule_suffix_len = 0;
+ char *comp = NULL;
+ int matched_val_len = 0;
+ int macro_len = 0;
+ int j = 0;
+ int has_macro_dn = 0;
+ int has_macro_levels = 0;
+
+ /* Determine what the rule's got once */
+ if ( strstr(rule, ACL_RULE_MACRO_DN_KEY) != NULL) {
+ has_macro_dn = 1;
+ }
+
+ if ( strstr(rule, ACL_RULE_MACRO_DN_LEVELS_KEY) != NULL) {
+ has_macro_levels = 1;
+ }
+
+ if ( !has_macro_dn && !has_macro_levels ) {
+
+ /*
+ * No $dn thing, just return a list with two elements, rule and NULL.
+ * charray_add will create the list and null terminate it.
+ */
+
+ charray_add( &a, slapi_ch_strdup(rule));
+ return(a);
+ } else {
+
+ /*
+ * Have an occurrence of the macro rules
+ *
+ * First, replace all occurrencers of ($dn) with the matched_val
+ */
+
+ if ( has_macro_dn) {
+ patched_rule =
+ acl_replace_str(rule, ACL_RULE_MACRO_DN_KEY, matched_val);
+ }
+
+ /* If there are no [$dn] we're done */
+
+ if ( !has_macro_levels ) {
+ charray_add( &a, patched_rule);
+ return(a);
+ } else {
+
+ /*
+ * It's a [$dn] type, so walk matched_val, splicing in all
+ * the suffix substrings and adding each such string to
+ * to the returned list.
+ * get_next_component() does not return the commas--the
+ * prefix and suffix should come with their commas.
+ *
+ * All occurrences of each [$dn] are replaced with each level.
+ *
+ * If has_macro_dn then patched_rule is the rule to strart with,
+ * and this needs to be freed at the end, otherwise
+ * just use rule.
+ */
+
+ if (patched_rule) {
+ rule_to_use = patched_rule;
+ } else {
+ rule_to_use = rule;
+ }
+
+ matched_val_len = strlen(matched_val);
+ j = 0;
+
+ while( j < matched_val_len) {
+
+ new_patched_rule =
+ acl_replace_str(rule_to_use, ACL_RULE_MACRO_DN_LEVELS_KEY,
+ &matched_val[j]);
+ charray_add( &a, new_patched_rule);
+
+ j += acl_find_comp_end(&matched_val[j]);
+ }
+
+ if (patched_rule) {
+ slapi_ch_free((void**)&patched_rule);
+ }
+
+ return(a);
+ }
+ }
+}
+
+/*
+ * Here, replace any occurrence of $attr.attrname with the
+ * value of attrname from lasinfo->resourceEntry.
+ *
+ *
+ * If there is no $attr thing then the returned list just contains rule
+ * itself.
+ *
+ * eg. rule: cn=fred,ou=*,ou=$attr.ou,o=sun.com
+ * ou: People
+ * ou: icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=sun.com
+ * cn=fred,ou=*,ou=icnc,o=sun.com
+ *
+*/
+
+static char **
+acllas_replace_attr_macro( char *rule, lasInfo *lasinfo) {
+
+ char **a = NULL;
+ char **working_list = NULL;
+ Slapi_Entry *e = lasinfo->resourceEntry;
+ char *str, *working_rule;
+ char *macro_str, *macro_attr_name;
+ int l;
+ Slapi_Attr *attr = NULL;
+
+ str = strstr(rule, ACL_RULE_MACRO_ATTR_KEY);
+ if ( str == NULL ) {
+
+ charray_add(&a, slapi_ch_strdup(rule));
+ return(a);
+
+ } else {
+
+ working_rule = slapi_ch_strdup(rule);
+ str = strstr(working_rule, ACL_RULE_MACRO_ATTR_KEY);
+ charray_add(&working_list, working_rule );
+
+ while( str != NULL) {
+
+ /*
+ * working_rule is the first member of working_list.
+ * str points to the next $attr.attrName in working_rule.
+ * each member of working_list needs to have each occurence of
+ * $attr.atrName replaced with the value of attrName in e.
+ * If attrName is multi valued then this generates another
+ * list which replaces the old one.
+ */
+
+ l = acl_strstr(&str[0], ")");
+ macro_str = slapi_ch_malloc(l+2);
+ strncpy( macro_str, &str[0], l+1);
+ macro_str[l+1] = '\0';
+
+ str = strstr(macro_str, ".");
+ str++; /* skip the . */
+ l = acl_strstr(&str[0], ")");
+ macro_attr_name = slapi_ch_malloc(l+1);
+ strncpy( macro_attr_name, &str[0], l);
+ macro_attr_name[l] = '\0';
+
+ slapi_entry_attr_find ( e, macro_attr_name, &attr );
+ if ( NULL == attr ) {
+
+ /*
+ * Here, if a $attr.attrName is such that the attrName
+ * does not occur in the entry then return a ""--
+ * this will go back to the matching code in
+ * aclutil_evaluate_macro() where "" will
+ * be taken as the candidate.
+ */
+
+ slapi_ch_free((void **)&macro_str);
+ slapi_ch_free((void **)&macro_attr_name);
+
+ charray_free(working_list);
+ charray_add(&a, slapi_ch_strdup(""));
+ return(a);
+
+ } else{
+
+ const struct berval *attrValue;
+ Slapi_Value *sval;
+ int i, j;
+ char *patched_rule;
+
+ a = NULL;
+ i= slapi_attr_first_value ( attr, &sval );
+ while(i != -1) {
+ attrValue = slapi_value_get_berval(sval);
+
+ j = 0;
+ while( working_list[j] != NULL) {
+
+ patched_rule =
+ acl_replace_str(working_list[j],
+ macro_str, attrValue->bv_val);
+ charray_add(&a, patched_rule);
+ j++;
+ }
+
+ i= slapi_attr_next_value( attr, i, &sval );
+ }/* while */
+
+ /*
+ * Here, a is working_list, where each member has had
+ * macro_str replaced with attrVal.
+ */
+
+ charray_free(working_list);
+ working_list = a;
+ working_rule = a[0];
+ }
+ slapi_ch_free((void **)&macro_str);
+ slapi_ch_free((void **)&macro_attr_name);
+
+ str = strstr(working_rule, ACL_RULE_MACRO_ATTR_KEY);
+
+ }/* while */
+
+ return(working_list);
+ }
+
+
+}
+
+/*
+ * returns ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW.
+ *
+ * user is a string from the userdn keyword which may contain
+ * * components. This routine does the compare component by component, so
+ * that * behaves differently to "normal".
+ * Any ($dn) or $attr must have been removed from user before this is called.
+*/
+static int
+acllas_eval_one_user( struct acl_pblock *aclpb, char * clientDN, char *rule) {
+
+ int exact_match = 0;
+ int ret_code = 0;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ char *s = NULL;
+
+
+
+ /* URL format */
+ if ((s = strchr (rule, '?'))!= NULL) {
+ /* URL format */
+ if (acllas__client_match_URL ( aclpb, clientDN,
+ rule) == ACL_TRUE) {
+ exact_match = 1;
+ }
+ } else if ( strstr(rule, "=*") == NULL ) {
+ /* Just a straight compare */
+ /* skip the ldap:/// part */
+ rule += LDAP_URL_prefix_len;
+ exact_match = !slapi_utf8casecmp((ACLUCHP)clientDN,
+ (ACLUCHP)rule);
+ } else{
+ /* Here, contains a =*, so need to match comp by comp */
+ /* skip the ldap:/// part */
+ rule += LDAP_URL_prefix_len;
+ ret_code = acl_match_prefix( rule, clientDN, &exact_match);
+ }
+ if ( exact_match) {
+ return( ACL_TRUE);
+ } else {
+ return(ACL_FALSE);
+ }
+}
+
+/*
+ * returns ACL_TRUE, ACL_FALSE and ACL_DONT_KNOW.
+ *
+ * The user string has had all ($dn) and $attr replaced
+ * so the only dodgy thing left is a *.
+ *
+ * If * appears in such a user string, then it matches only that
+ * component, not .*, like it would otherwise.
+ *
+*/
+static int
+acllas_eval_one_group(char *groupbuf, lasInfo *lasinfo) {
+
+ if (groupbuf) {
+ return( acllas__user_ismember_of_group (
+ lasinfo->aclpb,
+ groupbuf,
+ lasinfo->clientDn,
+ ACLLAS_CACHE_ALL_GROUPS,
+ lasinfo->aclpb->aclpb_clientcert
+ ));
+ } else {
+ return(ACL_FALSE); /* not in the empty group */
+ }
+}
+
+/*
+ * returns ACL_TRUE for match, ACL_FALSE for not a match, ACL_DONT_KNOW otherwise.
+*/
+static int
+acllas_eval_one_role(char *role, lasInfo *lasinfo) {
+
+ Slapi_DN *roleDN = NULL;
+ int rc = ACL_FALSE;
+ char ebuf [ BUFSIZ ];
+
+ /*
+ * See if lasinfo.clientDn has role rolebuf.
+ * Here we know it's not an anom user nor
+ * a an anyone user--the client dn must be matched against
+ * a real role.
+ */
+
+ roleDN = slapi_sdn_new_dn_byval(role);
+ if (role) {
+ rc = acllas__user_has_role(
+ lasinfo->aclpb,
+ roleDN,
+ lasinfo->aclpb->aclpb_authorization_sdn);
+ } else { /* The user does not have the empty role */
+ rc = ACL_FALSE;
+ }
+ slapi_sdn_free(&roleDN );
+
+ /* Some useful logging */
+ if (rc == ACL_TRUE ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "role evaluation: user '%s' does have role '%s'\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo->clientDn, ebuf),
+ role);
+ } else {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "role evaluation: user '%s' does NOT have role '%s'\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo->clientDn, ebuf),
+ role);
+ }
+ return(rc);
+}
+
+/*
+ * returns ACL_TRUE if e matches the filter str, ACL_FALSE if not,
+ * ACL_DONT_KNOW otherwise.
+*/
+static int acllas_eval_one_target_filter( char * str, Slapi_Entry *e) {
+
+ int rc = ACL_FALSE;
+ Slapi_Filter *f = NULL;
+
+ if ((f = slapi_str2filter(str)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Warning: Bad targetfilter(%s) in aci: does not match\n", str);
+ return(ACL_DONT_KNOW);
+ }
+
+ if (slapi_vattr_filter_test(NULL, e, f, 0 /*don't do acess chk*/)!= 0) {
+ rc = ACL_FALSE; /* Filter does not match */
+ } else {
+ rc = ACL_TRUE; /* filter does match */
+ }
+ slapi_filter_free(f, 1);
+
+ return(rc);
+
+}
+
+
+
+
+
+/***************************************************************************/
+/* E N D */
+/***************************************************************************/
diff --git a/ldap/servers/plugins/acl/acllist.c b/ldap/servers/plugins/acl/acllist.c
new file mode 100644
index 00000000..0147626d
--- /dev/null
+++ b/ldap/servers/plugins/acl/acllist.c
@@ -0,0 +1,940 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************************
+ *
+ * ACLLIST
+ *
+ * All the ACLs are read when the server is started. The ACLs are
+ * parsed and kept in an AVL tree. All the ACL List management are
+ * in this file.
+ *
+ * The locking on the aci cache is implemented using the acllist_acicache*()
+ * routines--a read/write lock.
+ *
+ * The granularity of the view of the cache is the entry level--ie.
+ * when an entry is being modified (mod,add,delete,modrdn) and the mod
+ * involves the aci attribute, then other operations will see the acl cache
+ * before the whole change or after the whole change, but not during the change.
+ * cf. acl.c:acl_modified()
+ *
+ * The only tricky issue is that there is also locking
+ * implemented for the anonymous profile and sometimes we need to take both
+ * locks cf. aclanom_anom_genProfile(). The rule is
+ * always take the acicache lock first, followed by the anon lock--following
+ * this rule will ensure no dead lock scenarios can arise.
+ *
+ * Some routines are called in different places with different lock
+ * contexts--for these routines acl_lock_flag_t is used to
+ * pass the context.
+ *
+ */
+#include "acl.h"
+
+static PRRWLock *aci_rwlock = NULL;
+#define ACILIST_LOCK_READ() PR_RWLock_Rlock (aci_rwlock )
+#define ACILIST_UNLOCK_READ() PR_RWLock_Unlock (aci_rwlock )
+#define ACILIST_LOCK_WRITE() PR_RWLock_Wlock (aci_rwlock )
+#define ACILIST_UNLOCK_WRITE() PR_RWLock_Unlock (aci_rwlock )
+
+
+/* Root of the TREE */
+static Avlnode *acllistRoot = NULL;
+
+#define CONTAINER_INCR 2000
+
+/* The container array */
+static AciContainer **aciContainerArray;
+static PRUint32 currContainerIndex =0;
+static PRUint32 maxContainerIndex = 0;
+static int curAciIndex = 1;
+
+/* PROTOTYPES */
+static int __acllist_add_aci ( aci_t *aci );
+static int __acllist_aciContainer_node_cmp ( caddr_t d1, caddr_t d2 );
+static int __acllist_aciContainer_node_dup ( caddr_t d1, caddr_t d2 );
+static void __acllist_free_aciContainer ( AciContainer **container);
+static void free_targetattrfilters( Targetattrfilter ***input_attrFilterArray);
+
+void my_print( Avlnode *root );
+
+
+int
+acllist_init ()
+{
+
+ if (( aci_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"ACLLIST LOCK") ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "acllist_init:failed in getting the rwlock\n" );
+ return 1;
+ }
+
+ aciContainerArray = (AciContainer **) slapi_ch_calloc ( 1,
+ CONTAINER_INCR * sizeof ( AciContainer * ) );
+ maxContainerIndex = CONTAINER_INCR;
+ currContainerIndex = 0;
+
+ return 0;
+}
+
+/*
+ * This is the callback for backend state changes.
+ * It needs to add/remove acis as backends come up and go down.
+ *
+ * The strategy is simple:
+ * When a backend moves to the SLAPI_BE_STATE_ON then we go get all the acis
+ * add them to the cache.
+ * When a backend moves out of the SLAPI_BE_STATE_ON then we remove them all.
+ *
+*/
+
+void acl_be_state_change_fnc ( void *handle, char *be_name, int old_state,
+ int new_state) {
+ Slapi_Backend *be=NULL;
+ const Slapi_DN *sdn;
+
+
+ if ( old_state == SLAPI_BE_STATE_ON && new_state != SLAPI_BE_STATE_ON) {
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Backend %s is no longer STARTED--deactivating it's acis\n",
+ be_name);
+
+ if ( (be = slapi_be_select_by_instance_name( be_name )) == NULL) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ /*
+ * Just get the first suffix--if there are multiple XXX ?
+ */
+
+ if ( (sdn = slapi_be_getsuffix( be, 0)) == NULL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ aclinit_search_and_update_aci ( 1, /* thisbeonly */
+ sdn, /* base */
+ be_name,/* be name */
+ LDAP_SCOPE_SUBTREE,
+ ACL_REMOVE_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+
+ } else if ( old_state != SLAPI_BE_STATE_ON && new_state == SLAPI_BE_STATE_ON) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Backend %s is now STARTED--activating it's acis\n", be_name);
+
+ if ( (be = slapi_be_select_by_instance_name( be_name )) == NULL) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ /*
+ * In fact there can onlt be one sufffix here.
+ */
+
+ if ( (sdn = slapi_be_getsuffix( be, 0)) == NULL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ aclinit_search_and_update_aci ( 1, /* thisbeonly */
+ sdn,
+ be_name, /* be name */
+ LDAP_SCOPE_SUBTREE,
+ ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ }
+
+}
+
+/* This routine must be called with the acicache write lock taken */
+int
+acllist_insert_aci_needsLock( const Slapi_DN *e_sdn, const struct berval* aci_attr)
+{
+
+ aci_t *aci;
+ char *acl_str;
+ int rv =0;
+
+ if (aci_attr->bv_len <= 0)
+ return 0;
+
+ aci = acllist_get_aci_new ();
+ slapi_sdn_set_ndn_byval ( aci->aci_sdn, slapi_sdn_get_ndn ( e_sdn ) );
+
+ acl_str = slapi_ch_strdup(aci_attr->bv_val);
+ /* Parse the ACL TEXT */
+ if ( 0 != (rv = acl_parse ( acl_str, aci )) ) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "ACL PARSE ERR(rv=%d): %s\n", rv, acl_str );
+ slapi_ch_free ( (void **) &acl_str );
+ acllist_free_aci ( aci );
+
+ return 1;
+ }
+
+ /* Now add it to the list */
+ if ( 0 != (rv =__acllist_add_aci ( aci ))) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACL ADD ACI ERR(rv=%d): %s\n", rv, acl_str );
+ slapi_ch_free ( (void **) &acl_str );
+ acllist_free_aci ( aci );
+ return 1;
+ }
+
+ slapi_ch_free ( (void **) &acl_str );
+ acl_regen_aclsignature ();
+ if ( aci->aci_elevel == ACI_ELEVEL_USERDN_ANYONE)
+ aclanom_invalidateProfile ();
+ return 0;
+}
+
+/* This routine must be called with the acicache write lock taken */
+static int
+__acllist_add_aci ( aci_t *aci )
+{
+
+ int rv = 0; /* OK */
+ AciContainer *aciListHead;
+ AciContainer *head;
+ PRUint32 i;
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_set_ndn_byval ( aciListHead->acic_sdn, slapi_sdn_get_ndn ( aci->aci_sdn ) );
+
+ /* insert the aci */
+ switch (avl_insert ( &acllistRoot, aciListHead, __acllist_aciContainer_node_cmp,
+ __acllist_aciContainer_node_dup ) ) {
+
+ case 1: /* duplicate ACL on the same entry */
+
+ /* Find the node that contains the acl. */
+ if ( NULL == (head = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ) ) ) {
+ slapi_log_error ( SLAPI_PLUGIN_ACL, plugin_name,
+ "Can't insert the acl in the tree\n");
+ rv = 1;
+ } else {
+ aci_t *t_aci;;
+
+ /* Attach the list */
+ t_aci = head->acic_list;;
+ while ( t_aci && t_aci->aci_next )
+ t_aci = t_aci->aci_next;
+
+ /* Now add the new one to the end of the list */
+ t_aci->aci_next = aci;
+ }
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Added the ACL:%s to existing container:[%d]%s\n",
+ aci->aclName, head->acic_index, slapi_sdn_get_ndn( head->acic_sdn ));
+
+ /* now free the tmp container */
+ aciListHead->acic_list = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+
+ break;
+ default:
+ /* The container is inserted. Now hook up the aci and setup the
+ * container index. Donot free the "aciListHead" here.
+ */
+ aciListHead->acic_list = aci;
+
+ /*
+ * First, see if we have an open slot or not - -if we have reuse it
+ */
+ i = 0;
+ while ( (i < currContainerIndex) && aciContainerArray[i] )
+ i++;
+
+ if ( currContainerIndex >= (maxContainerIndex - 2)) {
+ maxContainerIndex += CONTAINER_INCR;
+ aciContainerArray = (AciContainer **) slapi_ch_realloc ( (char *) aciContainerArray,
+ maxContainerIndex * sizeof ( AciContainer * ) );
+ }
+ aciListHead->acic_index = i;
+ /* If i < currContainerIndex, we are just re-using an old slot. */
+ /* We don't need to increase currContainerIndex if we just re-use an old one. */
+ if (i == currContainerIndex)
+ currContainerIndex++;
+
+ aciContainerArray[ aciListHead->acic_index ] = aciListHead;
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Added %s to container:%d\n",
+ slapi_sdn_get_ndn( aciListHead->acic_sdn ), aciListHead->acic_index );
+ break;
+ }
+
+ return rv;
+}
+
+
+
+static int
+__acllist_aciContainer_node_cmp ( caddr_t d1, caddr_t d2 )
+{
+
+ int rc =0;
+ AciContainer *c1 = (AciContainer *) d1;
+ AciContainer *c2 = (AciContainer *) d2;
+
+
+ rc = slapi_sdn_compare ( c1->acic_sdn, c2->acic_sdn );
+ return rc;
+}
+
+static int
+__acllist_aciContainer_node_dup ( caddr_t d1, caddr_t d2 )
+{
+
+ /* we allow duplicates -- they are not exactly duplicates
+ ** but multiple aci value on the same node
+ */
+ return 1;
+
+}
+
+
+/*
+ * Remove the ACL
+ *
+ * This routine must be called with the aclcache write lock taken.
+ * It takes in addition the one for the anom profile taken in
+ * aclanom_invalidateProfile().
+ * They _must_ be taken in this order or there
+ * is a deadlock scenario with aclanom_gen_anomProfile() which
+ * also takes them is this order.
+*/
+
+int
+acllist_remove_aci_needsLock( const Slapi_DN *sdn, const struct berval *attr )
+{
+
+ aci_t *head, *next;
+ int rv = 0;
+ AciContainer *aciListHead, *root;
+ AciContainer *dContainer;
+ int removed_anom_acl = 0;
+
+ /* we used to delete the ACL by value but we don't do that anymore.
+ * rather we delete all the acls in that entry and then repopulate it if
+ * there are any more acls.
+ */
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_set_ndn_byval ( aciListHead->acic_sdn, slapi_sdn_get_ndn ( sdn ) );
+
+ /* now find it */
+ if ( NULL == (root = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ))) {
+ /* In that case we don't have any acl for this entry. cool !!! */
+
+ __acllist_free_aciContainer ( &aciListHead );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "No acis to remove in this entry\n" );
+ return 0;
+ }
+
+ head = root->acic_list;
+ if ( head)
+ next = head->aci_next;
+ while ( head ) {
+ if ( head->aci_elevel == ACI_ELEVEL_USERDN_ANYONE)
+ removed_anom_acl = 1;
+
+ /* Free the acl */
+ acllist_free_aci ( head );
+
+ head = next;
+ next = NULL;
+ if ( head && head->aci_next )
+ next = head->aci_next;
+ }
+ root->acic_list = NULL;
+
+ /* remove the container from the slot */
+ aciContainerArray[root->acic_index] = NULL;
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Removing container[%d]=%s\n", root->acic_index,
+ slapi_sdn_get_ndn ( root->acic_sdn) );
+ dContainer = (AciContainer *) avl_delete ( &acllistRoot, aciListHead,
+ __acllist_aciContainer_node_cmp );
+ __acllist_free_aciContainer ( &dContainer );
+
+ acl_regen_aclsignature ();
+ if ( removed_anom_acl )
+ aclanom_invalidateProfile ();
+
+ /*
+ * Now read back the entry and repopulate ACLs for that entry, but
+ * only if a specific aci was deleted, otherwise, we do a
+ * "When Harry met Sally" and nail 'em all.
+ */
+
+ if ( attr != NULL) {
+
+ if (0 != (rv = aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name */
+ LDAP_SCOPE_BASE,
+ ACL_ADD_ACIS,
+ DONT_TAKE_ACLCACHE_WRITELOCK))) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ " Can't add the rest of the acls for entry:%s after delete\n",
+ slapi_sdn_get_dn ( sdn ) );
+ }
+ }
+
+ /* Now free the tmp container we used */
+ __acllist_free_aciContainer ( &aciListHead );
+
+ /*
+ * regenerate the anonymous profile if we have deleted
+ * anyone acls.
+ * We don't need the aclcache readlock because the context of
+ * this routine is we have the write lock already.
+ */
+ if ( removed_anom_acl )
+ aclanom_gen_anomProfile(DONT_TAKE_ACLCACHE_READLOCK);
+
+ return rv;
+}
+
+AciContainer *
+acllist_get_aciContainer_new ( )
+{
+
+ AciContainer *head;
+
+ head = (AciContainer * ) slapi_ch_calloc ( 1, sizeof ( AciContainer ) );
+ head->acic_sdn = slapi_sdn_new ( );
+ head->acic_index = -1;
+
+ return head;
+}
+static void
+__acllist_free_aciContainer ( AciContainer **container)
+{
+
+ PR_ASSERT ( container != NULL );
+
+ if ( (*container)->acic_index >= 0 )
+ aciContainerArray[ (*container)->acic_index] = NULL;
+ if ( (*container)->acic_sdn )
+ slapi_sdn_free ( &(*container)->acic_sdn );
+ slapi_ch_free ( (void **) container );
+
+}
+
+void
+acllist_done_aciContainer ( AciContainer *head )
+{
+
+ PR_ASSERT ( head != NULL );
+
+ slapi_sdn_done ( head->acic_sdn );
+ head->acic_index = -1;
+
+ /* The caller is responsible for taking care of list */
+ head->acic_list = NULL;
+}
+
+
+aci_t *
+acllist_get_aci_new ()
+{
+ aci_t *aci_item;
+
+ aci_item = (aci_t *) slapi_ch_calloc (1, sizeof (aci_t));
+ aci_item->aci_sdn = slapi_sdn_new ();
+ aci_item->aci_index = curAciIndex++;
+ aci_item->aci_elevel = ACI_DEFAULT_ELEVEL; /* by default it's a complex */
+ aci_item->targetAttr = (Targetattr **) slapi_ch_calloc (
+ ACL_INIT_ATTR_ARRAY,
+ sizeof (Targetattr *));
+ return aci_item;
+}
+
+void
+acllist_free_aci(aci_t *item)
+{
+
+ Targetattr **attrArray;
+
+ /* The caller is responsible for taking
+ ** care of list issue
+ */
+ if (item == NULL) return;
+
+ slapi_sdn_free ( &item->aci_sdn );
+ slapi_filter_free (item->target, 1);
+
+ /* slapi_filter_free(item->targetAttr, 1); */
+ attrArray = item->targetAttr;
+ if (attrArray) {
+ int i = 0;
+ Targetattr *attr;
+
+ while (attrArray[i] != NULL) {
+ attr = attrArray[i];
+ if (attr->attr_type & ACL_ATTR_FILTER) {
+ slapi_filter_free(attr->u.attr_filter, 1);
+ } else {
+ slapi_ch_free ( (void **) &attr->u.attr_str );
+ }
+ slapi_ch_free ( (void **) &attr );
+ i++;
+ }
+ /* Now free the array */
+ slapi_ch_free ( (void **) &attrArray );
+ }
+
+ /* Now free any targetattrfilters in this aci item */
+
+ if ( item->targetAttrAddFilters ) {
+ free_targetattrfilters(&item->targetAttrAddFilters);
+ }
+
+ if ( item->targetAttrDelFilters ) {
+ free_targetattrfilters(&item->targetAttrDelFilters);
+ }
+
+ if (item->targetFilterStr) slapi_ch_free ( (void **) &item->targetFilterStr );
+ slapi_filter_free(item->targetFilter, 1);
+
+ /* free the handle */
+ if (item->aci_handle) ACL_ListDestroy(NULL, item->aci_handle);
+
+ /* Free the name */
+ if (item->aclName) slapi_ch_free((void **) &item->aclName);
+
+ /* Free any macro info*/
+ if (item->aci_macro) {
+ slapi_ch_free((void **) &item->aci_macro->match_this);
+ slapi_ch_free((void **) &item->aci_macro);
+ }
+
+ /* free at last -- free at last */
+ slapi_ch_free ( (void **) &item );
+}
+
+static void free_targetattrfilters( Targetattrfilter ***attrFilterArray) {
+
+ if (*attrFilterArray) {
+ int i = 0;
+ Targetattrfilter *attrfilter;
+
+ while ((*attrFilterArray)[i] != NULL) {
+ attrfilter = (*attrFilterArray)[i];
+
+ if ( attrfilter->attr_str != NULL) {
+ slapi_ch_free ( (void **) &attrfilter->attr_str );
+ }
+
+ if (attrfilter->filter != NULL) {
+ slapi_filter_free(attrfilter->filter, 1);
+ }
+
+ if( attrfilter->filterStr != NULL) {
+ slapi_ch_free ( (void **) &attrfilter->filterStr );
+ }
+
+ slapi_ch_free ( (void **) &attrfilter );
+ i++;
+ }
+ /* Now free the array */
+ slapi_ch_free ( (void **) attrFilterArray );
+ }
+
+}
+
+/* SEARCH */
+void
+acllist_init_scan (Slapi_PBlock *pb, int scope, char *base)
+{
+ Acl_PBlock *aclpb;
+ int i;
+ AciContainer *root;
+ char *basedn = NULL;
+ int index;
+
+ if ( acl_skip_access_check ( pb, NULL ) ) {
+ return;
+ }
+
+ /*acllist_print_tree ( acllistRoot, &depth, "top", "top") ; */
+ /* my_print ( acllistRoot );*/
+ /* If we have an anonymous profile and I am an anom dude - let's skip it */
+ if ( aclanom_is_client_anonymous ( pb )) {
+ return;
+ }
+ aclpb = acl_get_aclpb (pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 4 \n" );
+ return;
+ }
+
+ aclpb->aclpb_handles_index[0] = -1;
+
+ /* If base is NULL - it means we are going to go thru all the ACLs
+ * This is needed when we do anonymous profile generation.
+ */
+ if ( NULL == base ) {
+ return;
+ }
+
+ aclpb->aclpb_state |= ACLPB_SEARCH_BASED_ON_LIST ;
+
+ acllist_acicache_READ_LOCK();
+
+ basedn = slapi_ch_strdup (base);
+ index = 0;
+ aclpb->aclpb_search_base = slapi_ch_strdup ( base );
+
+ while (basedn ) {
+ char *tmp = NULL;
+
+ slapi_sdn_set_ndn_byref ( aclpb->aclpb_aclContainer->acic_sdn, basedn );
+
+ root = (AciContainer *) avl_find( acllistRoot,
+ (caddr_t) aclpb->aclpb_aclContainer,
+ (IFP) __acllist_aciContainer_node_cmp);
+ if ( index >= ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[0] = -1;
+ slapi_ch_free ( (void **) &basedn);
+ break;
+ } else if ( NULL != root ) {
+ aclpb->aclpb_base_handles_index[index++] = root->acic_index;
+ aclpb->aclpb_base_handles_index[index] = -1;
+ }
+ tmp = slapi_dn_parent ( basedn );
+ slapi_ch_free ( (void **) &basedn);
+ basedn = tmp;
+ }
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer);
+
+ if ( aclpb->aclpb_base_handles_index[0] == -1 )
+ aclpb->aclpb_state &= ~ACLPB_SEARCH_BASED_ON_LIST ;
+
+ acllist_acicache_READ_UNLOCK();
+
+ i = 0;
+ while ( i < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_base_handles_index[i] != -1 ) {
+ i++;
+ }
+}
+
+/*
+ * Initialize aclpb_handles_index[] (sentinel -1) to
+ * contain a list of all aci items at and above edn in the DIT tree.
+ * This list will be subsequestly scanned to find applicable aci's for
+ * the given operation.
+*/
+
+void
+acllist_aciscan_update_scan ( Acl_PBlock *aclpb, char *edn )
+{
+
+ int i, index = 0;
+ char *basedn = NULL;
+ AciContainer *root;
+ int is_not_search_base = 1;
+
+
+ /* First copy the containers indx from the base to the one which is
+ * going to be used.
+ * The base handles get done in acllist_init_scan().
+ * This stuff is only used if it's a search operation.
+ */
+ if ( aclpb && aclpb->aclpb_search_base ) {
+ while ( aclpb->aclpb_base_handles_index[index] != -1 &&
+ index < ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[index] =
+ aclpb->aclpb_base_handles_index[index];
+ index++;
+ }
+ if ( strcasecmp ( edn, aclpb->aclpb_search_base) == 0) {
+ is_not_search_base = 0;
+ }
+ }
+ aclpb->aclpb_handles_index[index] = -1;
+
+ /*
+ * Here, make a list of all the aci's that will apply
+ * to edn ie. all aci's at and above edn in the DIT tree.
+ *
+ * Do this by walking up edn, looking at corresponding
+ * points in the acllistRoot aci tree.
+ *
+ * If is_not_search_base is true, then we need to iterate on edn, otherwise
+ * we've already got all the base handles above.
+ *
+ */
+
+ if (is_not_search_base) {
+
+ basedn = slapi_ch_strdup ( edn );
+
+ while (basedn ) {
+ char *tmp = NULL;
+
+ slapi_sdn_set_ndn_byref ( aclpb->aclpb_aclContainer->acic_sdn, basedn );
+
+ root = (AciContainer *) avl_find( acllistRoot,
+ (caddr_t) aclpb->aclpb_aclContainer,
+ (IFP) __acllist_aciContainer_node_cmp);
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching AVL tree for update:%s: container:%d\n", basedn ,
+ root ? root->acic_index: -1);
+ if ( index >= ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[0] = -1;
+ slapi_ch_free ( (void **) &basedn);
+ break;
+ } else if ( NULL != root ) {
+ aclpb->aclpb_handles_index[index++] = root->acic_index;
+ aclpb->aclpb_handles_index[index] = -1;
+ }
+ tmp = slapi_dn_parent ( basedn );
+ slapi_ch_free ( (void **) &basedn);
+ basedn = tmp;
+ if ( aclpb->aclpb_search_base && tmp &&
+ ( 0 == strcasecmp ( tmp, aclpb->aclpb_search_base))) {
+ slapi_ch_free ( (void **) &basedn);
+ tmp = NULL;
+ }
+ } /* while */
+ }
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer );
+ i = 0;
+ while ( i < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[i] != -1 ) {
+ i++;
+ }
+
+}
+
+aci_t *
+acllist_get_first_aci (Acl_PBlock *aclpb, PRUint32 *cookie )
+{
+
+ int val;
+
+ *cookie = val = 0;
+ if ( aclpb && aclpb->aclpb_handles_index[0] != -1 ) {
+ val = aclpb->aclpb_handles_index[*cookie];
+ }
+ if ( NULL == aciContainerArray[val]) {
+ return ( acllist_get_next_aci ( aclpb, NULL, cookie ) );
+ }
+
+ return (aciContainerArray[val]->acic_list );
+}
+/*
+ * acllist_get_next_aci
+ * Return the next aci in the list
+ *
+ * Inputs
+ * Acl_PBlock *aclpb -- acl Main block; if aclpb= NULL,
+ * -- then we scan thru the whole list.
+ * -- which is used by anom profile code.
+ * aci_t *curaci -- the current aci
+ * PRUint32 *cookie -- cookie -- to maintain a state (what's next)
+ *
+ */
+
+aci_t *
+acllist_get_next_aci ( Acl_PBlock *aclpb, aci_t *curaci, PRUint32 *cookie )
+{
+ PRUint32 val;
+ int scan_entire_list;
+
+ /*
+ Here, if we're passed a curaci and there's another aci in the same node,
+ return that one.
+ */
+
+ if ( curaci && curaci->aci_next )
+ return ( curaci->aci_next );
+
+ /*
+ Determine if we need to scan the entire list of acis.
+ We do if the aclpb==NULL or if the first handle index is -1.
+ That means that we want to go through
+ the entire aciContainerArray up to the currContainerIndex to get
+ acis; the -1 in the first position is a special keyword which tells
+ us that the acis have changed, so we need to go through all of them.
+ */
+
+ scan_entire_list = (aclpb == NULL || aclpb->aclpb_handles_index[0] == -1);
+
+start:
+ (*cookie)++;
+ val = *cookie;
+
+ /* if we are not scanning the entire aciContainerArray list, we only want to
+ look at the indexes specified in the handles index */
+ if ( !scan_entire_list )
+ val = aclpb->aclpb_handles_index[*cookie];
+
+ /* the hard max end */
+ if ( val >= maxContainerIndex)
+ return NULL;
+
+ /* reached the end of the array */
+ if ((!scan_entire_list && (*cookie >= (ACLPB_MAX_SELECTED_ACLS-1))) ||
+ (*cookie >= currContainerIndex)) {
+ return NULL;
+ }
+
+ /* if we're only using the handles list for our aciContainerArray
+ indexes, the -1 value marks the end of that list */
+ if ( !scan_entire_list && (aclpb->aclpb_handles_index[*cookie] == -1) ) {
+ return NULL;
+ }
+
+ /* if we're scanning the entire list, and we hit a null value in the
+ middle of the list, just try the next one; this can happen if
+ an aci was deleted - it can leave "holes" in the array */
+ if ( scan_entire_list && ( NULL == aciContainerArray[val])) {
+ goto start;
+ }
+
+ if ( aciContainerArray[val] )
+ return (aciContainerArray[val]->acic_list );
+ else
+ return NULL;
+}
+
+void
+acllist_acicache_READ_UNLOCK( )
+{
+ ACILIST_UNLOCK_READ ();
+
+}
+
+void
+acllist_acicache_READ_LOCK()
+{
+ /* get a reader lock */
+ ACILIST_LOCK_READ ();
+
+}
+
+void
+acllist_acicache_WRITE_UNLOCK( )
+{
+ ACILIST_UNLOCK_WRITE ();
+
+}
+
+void
+acllist_acicache_WRITE_LOCK( )
+{
+ ACILIST_LOCK_WRITE ();
+
+}
+
+/* This routine must be called with the acicache write lock taken */
+int
+acllist_moddn_aci_needsLock ( Slapi_DN *oldsdn, char *newdn )
+{
+
+
+ AciContainer *aciListHead;
+ AciContainer *head;
+
+ /* first get the container */
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_free(&aciListHead->acic_sdn);
+ aciListHead->acic_sdn = oldsdn;
+
+
+ if ( NULL == (head = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ) ) ) {
+
+ slapi_log_error ( SLAPI_PLUGIN_ACL, plugin_name,
+ "Can't find the acl in the tree for moddn operation:olddn%s\n",
+ slapi_sdn_get_ndn ( oldsdn ));
+ aciListHead->acic_sdn = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+ return 1;
+ }
+
+
+ /* Now set the new DN */
+ slapi_sdn_done ( head->acic_sdn );
+ slapi_sdn_set_ndn_byval ( head->acic_sdn, newdn );
+
+ aciListHead->acic_sdn = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+
+ return 0;
+}
+
+void
+acllist_print_tree ( Avlnode *root, int *depth, char *start, char *side)
+{
+
+ AciContainer *aciHeadList;
+
+ if ( NULL == root ) {
+ return;
+ }
+ aciHeadList = (AciContainer *) root->avl_data;
+ slapi_log_error ( SLAPI_LOG_ACL, "plugin_name",
+ "Container[ Depth=%d%s-%s]: %s\n", *depth, start, side,
+ slapi_sdn_get_ndn ( aciHeadList->acic_sdn ) );
+
+ (*depth)++;
+
+ acllist_print_tree ( root->avl_left, depth, side, "L" );
+ acllist_print_tree ( root->avl_right, depth, side, "R" );
+
+ (*depth)--;
+
+}
+
+static
+void
+ravl_print( Avlnode *root, int depth )
+{
+ int i;
+
+ AciContainer *aciHeadList;
+ if ( root == 0 )
+ return;
+
+ ravl_print( root->avl_right, depth+1 );
+
+ for ( i = 0; i < depth; i++ )
+ printf( " " );
+ aciHeadList = (AciContainer *) root->avl_data;
+ printf( "%s\n", slapi_sdn_get_ndn ( aciHeadList->acic_sdn ) );
+
+ ravl_print( root->avl_left, depth+1 );
+}
+
+void
+my_print( Avlnode *root )
+{
+ printf( "********\n" );
+
+ if ( root == 0 )
+ printf( "\tNULL\n" );
+ else
+ ( void ) ravl_print( root, 0 );
+
+ printf( "********\n" );
+}
diff --git a/ldap/servers/plugins/acl/aclparse.c b/ldap/servers/plugins/acl/aclparse.c
new file mode 100644
index 00000000..2247471a
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclparse.c
@@ -0,0 +1,1928 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int __aclp__parse_aci(char *str, aci_t *aci_item);
+static int __aclp__sanity_check_acltxt(aci_t *aci_item, char *str);
+static char * __aclp__normalize_acltxt (aci_t *aci_item, char *str);
+static char * __aclp__getNextLASRule(aci_t *aci_item, char *str,
+ char **endOfCurrRule);
+static char * __aclp__dn_normalize( char *dn , char *end);
+static int __aclp__get_aci_right ( char *str);
+static int __aclp__init_targetattr (aci_t *aci, char *attr_val);
+static int __acl__init_targetattrfilters( aci_t *aci_item, char *str);
+static int process_filter_list( Targetattrfilter ***attrfilterarray,
+ char * str);
+static int __acl_init_targetattrfilter( Targetattrfilter *attrfilter, char *str );
+static void __aclp_chk_paramRules ( aci_t *aci_item, char *start,
+ char *end);
+static void __acl_strip_trailing_space( char *str);
+static void __acl_strip_leading_space( char **str);
+static char * __acl_trim_filterstr( char * str );
+static int acl_verify_exactly_one_attribute( char *attr_name, Slapi_Filter *f);
+static int type_compare( Slapi_Filter *f, void *arg);
+static int acl_check_for_target_macro( aci_t *aci_item, char *value);
+static int get_acl_rights_as_int( char * strValue);
+
+/***************************************************************************
+*
+* acl_parse
+*
+* Parses the input string and copies the information into the
+* correct place in the aci.
+*
+*
+* Input:
+* char *str - Input string which has the ACL
+* This is a duped copy, so here we have
+* the right to stich '\0' characters into str for
+* processing purposes. If you want to keep
+* a piece of str, you'll need to dup it
+* as it gets freed outside the scope of acl_parse.
+* aci_t *item - the aci item where the ACL info will be
+* - stored.
+*
+* Returns:
+* 0 -- Parsed okay
+* < 0 -- error codes
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_parse(char * str, aci_t *aci_item)
+{
+
+ int rv=0;
+ char *next;
+ char *save;
+
+ while(*str) {
+ __acl_strip_leading_space( &str );
+ if (*str == '\0') break;
+
+ if (*str == '(') {
+ if ((next = slapi_find_matching_paren(str)) == NULL) {
+ return(ACL_SYNTAX_ERR);
+ }
+ } else {
+ /* then we have done all the processing */
+ return 0;
+ }
+ LDAP_UTF8INC(str); /* skip the "(" */
+ save = next;
+ LDAP_UTF8INC(next);
+ *save = '\0';
+
+ /* Now we have a "str)" */
+ if ( 0 != (rv = __aclp__parse_aci(str, aci_item))) {
+ return(rv);
+ }
+
+ /* Move to the next */
+ str = next;
+ }
+
+ /* check if have a ACLTXT or not */
+ if (!(aci_item->aci_type & ACI_ACLTXT))
+ return ACL_SYNTAX_ERR;
+
+ if (aci_item->target) {
+ Slapi_Filter *f;
+
+ /* Make sure that the target is a valid target.
+ ** Example: ACL is located in
+ ** "ou=engineering, o=ace industry, c=us
+ ** but if the target is "o=ace industry, c=us",
+ ** then it's an ERROR.
+ */
+ f = aci_item->target;
+ if (aci_item->aci_type & ACI_TARGET_DN) {
+ char *avaType;
+ struct berval *avaValue;
+ const char *dn;
+
+ dn = slapi_sdn_get_ndn ( aci_item->aci_sdn );
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ if (!slapi_dn_issuffix( avaValue->bv_val, dn))
+ return ACL_INVALID_TARGET;
+ }
+ }
+
+ /*
+ ** We need to keep the taregetFilterStr for anyone ACL only.
+ ** same for targetValueFilterStr.
+ ** We need to keep it for macros too as it needs to be expnaded at eval time.
+ **
+ */
+ if ( (aci_item->aci_elevel != ACI_ELEVEL_USERDN_ANYONE) &&
+ !(aci_item->aci_type & ACI_TARGET_MACRO_DN) ) {
+ slapi_ch_free ( (void **) & aci_item->targetFilterStr );
+ }
+
+ /*
+ * If we parsed the aci and there was a ($dn) on the user side
+ * but none in hte taget then that's an error as the user side
+ * value is derived from the target side value.
+ */
+
+ if (!(aci_item->aci_type & ACI_TARGET_MACRO_DN) &&
+ (aci_item->aci_ruleType & ACI_PARAM_DNRULE)) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl_parse: A macro in a subject ($dn) must have a macro in the target.\n");
+ return(ACL_INVALID_TARGET);
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+*
+* __aclp__parse_aci
+*
+* Parses Each individual subset of information/
+*
+* Input:
+* char *str - Input string which has the ACL like "str)"
+* aci_t *item - the aci item where the ACL info will be
+* - stored.
+*
+* Returns:
+* 0 -- Parsed okay
+* < 0 -- error codes
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclp__parse_aci (char *str, aci_t *aci_item)
+{
+
+ int len;
+ int rv;
+ int type;
+ char *tmpstr;
+ char *s = NULL;
+ char *value = NULL;
+ Slapi_Filter *f = NULL;
+ int targetattrlen = strlen(aci_targetattr);
+ int targetdnlen = strlen (aci_targetdn);
+ int tfilterlen = strlen(aci_targetfilter);
+ int targetattrfilterslen = strlen(aci_targetattrfilters);
+
+ __acl_strip_leading_space( &str );
+
+ if (*str == '\0') {
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /* The first letter should tell us something */
+ switch(*str) {
+ case 'v':
+ type = ACI_ACLTXT;
+
+ if ( 0 != (rv= __aclp__sanity_check_acltxt(aci_item, str ) ) ) {
+
+ return rv;
+ }
+ break;
+
+ case 't':
+ if (strncmp(str, aci_targetattrfilters,targetattrfilterslen ) == 0) {
+ type = ACI_TARGET_ATTR;
+
+
+ /*
+ * The targetattrfilters bit looks like this:
+ * (targetattrfilters="add= attr1:F1 && attr2:F2 ... && attrn:Fn,
+ * del= attr1:F1 && attr2:F2... && attrn:Fn")
+ */
+ if ( 0 != (rv= __acl__init_targetattrfilters(
+ aci_item, str))) {
+ return rv;
+ }
+ } else if (strncmp(str, aci_targetattr,targetattrlen ) == 0) {
+ type = ACI_TARGET_ATTR;
+
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_ATTR_NOT;
+ strncpy(s, " ", 1);
+ }
+ /* Get individual components of the targetattr.
+ * (targetattr = "cn || u* || phone ||tel:add:(tel=1234)
+ * || sn:del:(gn=5678)")
+ * If it contains a value filter, the type will also be
+ * ACI_TARGET_VALUE_ATTR.
+ */
+ if ( 0 != (rv= __aclp__init_targetattr(
+ aci_item, str))) {
+ return rv;
+ }
+ } else if (strncmp(str, aci_targetfilter,tfilterlen ) == 0) {
+ if ( aci_item->targetFilter)
+ return ACL_SYNTAX_ERR;
+
+ type = ACI_TARGET_FILTER;
+ /* we need to remove the targetfilter stuff*/
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_FILTER_NOT;
+ }
+
+ /*
+ * If it's got a macro in the targetfilter then it must
+ * have a target and it must have a macro.
+ */
+
+ if ((s = strstr (str, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ ((s = strstr(str, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL)) {
+
+ /* Must have a targetmacro */
+ if ( !(aci_item->aci_type & ACI_TARGET_MACRO_DN)) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl_parse: A macro in a targetfilter ($dn) must have a macro in the target.\n");
+ return(ACL_SYNTAX_ERR);
+ }
+
+ type|= ACI_TARGET_FILTER_MACRO_DN;
+ }
+
+ tmpstr = strchr(str, '=');
+ tmpstr++;
+ __acl_strip_leading_space(&tmpstr);
+
+ /*
+ * Trim off enclosing quotes and enclosing
+ * superfluous brackets.
+ * The result has been duped so it can be kept.
+ */
+
+ tmpstr = __acl_trim_filterstr( tmpstr );
+
+ f = slapi_str2filter(tmpstr);
+
+ /* save the filter string */
+ aci_item->targetFilterStr = tmpstr;
+
+ } else if (strncmp(str, aci_targetdn, targetdnlen) == 0) {
+ char *tstr = NULL;
+ const size_t LDAP_URL_prefix_len = strlen (LDAP_URL_prefix);
+ char *tt;
+ type = ACI_TARGET_DN;
+ /* Keep a copy of the target attr */
+ if (aci_item->target) {
+ return (ACL_SYNTAX_ERR);
+ }
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_NOT;
+ strncpy(s, " ", 1);
+ }
+
+ /* Convert it to lower as slapi_dn_normalize() does not */
+ for (tt = str; *tt; tt++) *tt = TOLOWER ( *tt );
+
+ if ( (s = strchr( str, '=' )) != NULL ) {
+ value = s + 1;
+ slapi_dn_normalize(value);
+ len = strlen ( value );
+ if (*value == '"' && value[len-1] == '"'){
+ value[len-1] = '\0';
+ value++;
+ }
+ __acl_strip_leading_space(&value);
+ } else {
+ return ( ACL_SYNTAX_ERR );
+ }
+
+ if ( strncasecmp ( value, LDAP_URL_prefix , LDAP_URL_prefix_len) )
+ return ( ACL_SYNTAX_ERR );
+
+ value += LDAP_URL_prefix_len;
+ len = strlen ( value );
+ tstr = (char *) slapi_ch_malloc ( targetdnlen + len + 4 );
+ sprintf ( tstr, "(target=%s)", value);
+ if ( (rv = acl_check_for_target_macro( aci_item, value)) == -1) {
+ slapi_ch_free ( (void **) &tstr );
+ return(ACL_SYNTAX_ERR);
+ } else if ( rv > 0) {
+ /* is present, so the type is now ACL_TARGET_MACRO_DN */
+ type = ACI_TARGET_MACRO_DN;
+ } else {
+ /* it's a normal target with no macros inside */
+ f = slapi_str2filter ( tstr );
+ }
+ slapi_ch_free ( (void **) &tstr );
+ } else {
+ /* did start with a 't' but was not a recognsied keyword */
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /*
+ * Here, it was a recognised keyword that started with 't'.
+ * Check that the filter associated with ACI_TARGET_DN and
+ * ACI_TARGET_FILTER are OK.
+ */
+ if (f == NULL) {
+ /* The following types require a filter to have been created */
+ if (type & ACI_TARGET_DN)
+ return ACL_TARGET_FILTER_ERR;
+ else if (type & ACI_TARGET_FILTER)
+ return ACL_TARGETFILTER_ERR;
+ } else {
+ int filterChoice;
+
+ filterChoice = slapi_filter_get_choice ( f );
+ if ( (type & ACI_TARGET_DN) &&
+ ( filterChoice == LDAP_FILTER_PRESENT)) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__parse_aci: Unsupported filter type:%d\n", filterChoice);
+ return(ACL_SYNTAX_ERR);
+ } else if (( filterChoice == LDAP_FILTER_SUBSTRINGS) &&
+ (type & ACI_TARGET_DN)) {
+ type &= ~ACI_TARGET_DN;
+ type |= ACI_TARGET_PATTERN;
+ }
+ }
+
+ if ((type & ACI_TARGET_DN) ||
+ (type & ACI_TARGET_PATTERN)) {
+ if (aci_item->target) {
+ /* There is something already. ERROR */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Multiple targets in the ACL syntax\n",
+ 0,0,0);
+ slapi_filter_free(f, 1);
+ return(ACL_SYNTAX_ERR);
+ } else {
+ aci_item->target = f;
+ }
+ } else if ( type & ACI_TARGET_FILTER) {
+ if (aci_item->targetFilter) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Multiple target Filters in the ACL Syntax\n",
+ 0,0,0);
+ slapi_filter_free(f, 1);
+ return(ACL_SYNTAX_ERR);
+ } else {
+ aci_item->targetFilter = f;
+ }
+ }
+ break; /* 't' */
+ default:
+ /* Here the keyword did not start with 'v' ot 't' so error */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Unknown keyword at \"%s\"\n Expecting"
+ " \"target\", \"targetattr\", \"targetfilter\", \"targattrfilters\""
+ " or \"version\"\n", str, 0, 0);
+ return(ACL_SYNTAX_ERR);
+ }/* switch() */
+
+ /* Store the type info */
+ aci_item->aci_type |= type;
+
+ return 0;
+}
+
+/***************************************************************************
+* acl__sanity_check_acltxt
+*
+* Check the input ACL text. Reports any errors. Also forgivs if certain
+* things are missing.
+*
+* Input:
+* char *str - String containg the acl text
+* int *err - error status
+*
+* Returns:
+* 0 --- good status
+* <0 --- error
+*
+* Error Handling:
+* None.
+*
+*
+**************************************************************************/
+static int
+__aclp__sanity_check_acltxt (aci_t *aci_item, char *str)
+{
+ NSErr_t errp;
+ char *s;
+ ACLListHandle_t *handle = NULL;
+ char *newstr = NULL;
+ char *word;
+ char *next;
+
+ memset (&errp, 0, sizeof(NSErr_t));
+ newstr = str;
+
+ while ((s = strstr(newstr, "authenticate")) != NULL) {
+ char *next;
+ next = s + 12;
+ s--;
+ while (s != str && ldap_utf8isspace(s)) LDAP_UTF8DEC(s);
+ if (s && *s == ';') {
+ /* We don't support authenticate stuff */
+ return ACL_INVALID_AUTHORIZATION;
+
+ } else {
+ newstr = next;
+ }
+ }
+
+ newstr = slapi_ch_strdup (str);
+ word = ldap_utf8strtok_r(newstr, " ", &next);
+ if (strcasecmp (word, "version") == 0) {
+ word = ldap_utf8strtok_r(NULL, " ", &next);
+ if (atoi(word) != 3) {
+ slapi_ch_free ( (void **) &newstr );
+ return ACL_INCORRECT_ACI_VERSION;
+ }
+ }
+ slapi_ch_free ( (void **) &newstr );
+
+ /* We need to normalize the DNs in the userdn and group dn
+ ** so that, it's only done once.
+ */
+ if ((newstr = __aclp__normalize_acltxt (aci_item, str )) == NULL) {
+ return ACL_SYNTAX_ERR;
+ }
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Normalized String:%s\n", newstr, 0,0);
+
+ /* check for acl syntax error */
+ if ((handle = (ACLListHandle_t *) ACL_ParseString(&errp,
+ newstr)) == NULL) {
+ acl_print_acllib_err(&errp, str);
+ slapi_ch_free ( (void **) &newstr );
+ return ACL_SYNTAX_ERR;
+ } else {
+ /* get the rights and the aci type */
+ aci_item->aci_handle = handle;
+ nserrDispose(&errp);
+ slapi_ch_free ( (void **) &newstr );
+
+ return 0;
+ }
+}
+/******************************************************************************
+*
+* acl__normalize_acltxt
+*
+*
+* XXXrbyrne this routine should be re-written when someone eventually
+* gets sick enough of it. Same for getNextLAS() below.
+*
+* Normalize the acltxt i.e normalize all the DNs specified in the
+* Userdn and Groupdn rule so that we normalize once here and not
+* over and over again at the runtime in the LASes. We have to normalize
+* before we generate the handle otherwise it's of no use.
+* Also convert deny to deny absolute
+*
+* The string that comes in is something like:
+* version 3.0; acl "Dept domain administration"; allow (all)
+* groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP"; )
+*
+* Returns NULL on error.
+*
+******************************************************************************/
+static char *
+__aclp__normalize_acltxt ( aci_t * aci_item, char * str )
+{
+
+ char *s, *p;
+ char *end;
+ char *aclstr, *s_aclstr;
+ char *ret_str = NULL;
+ int len;
+ char *ptr, *aclName;
+ char *nextACE;
+ char *tmp_str = NULL;
+ char *acestr = NULL;
+ char *s_acestr = NULL;
+ int aci_rights_val = 0; /* bug 389975 */
+
+ /* make a copy first */
+ s_aclstr = aclstr = slapi_ch_strdup ( str );
+
+ /* The rules are like this version 3.0; acl "xyz"; rule1; rule2; */
+ s = strchr (aclstr, ';');
+ if ( NULL == s) {
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+ aclstr = ++s;
+
+ /* From DS 4.0, we support both aci (or aci) "name" -- we have to change to acl
+ ** as libaccess will not like it
+ */
+ s = aclstr;
+ while (s && ldap_utf8isspace(s)) LDAP_UTF8INC(s);
+ *(s+2 ) = 'l';
+
+ aclName = s+3;
+
+ s = strchr (aclstr, ';');
+ if ( NULL == s) {
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+
+ aclstr = s;
+ LDAP_UTF8INC(aclstr);
+ *s = '\0';
+
+ /* Here aclName is the acl description string */
+ aci_item->aclName = slapi_ch_strdup ( aclName );
+
+ aclutil_str_appened (&ret_str, s_aclstr);
+ aclutil_str_appened (&ret_str, ";");
+
+ /* start with the string */
+ acestr = aclstr;
+
+ /*
+ * Here acestr is something like:
+ *
+ * " allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP";)"
+ *
+ *
+ */
+
+normalize_nextACERule:
+
+ /* now we are in the rule part */
+ tmp_str = acestr;
+ s = strchr (tmp_str, ';');
+ if ( s == NULL) {
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+ nextACE = s;
+ LDAP_UTF8INC(nextACE);
+ *s = '\0';
+
+ /* acestr now will hold copy of the ACE. Also add
+ ** some more space in case we need to add "absolute"
+ ** for deny rule. We will never need more 2 times
+ ** the len.
+ */
+ len = strlen (tmp_str);
+ s_acestr = acestr = slapi_ch_calloc ( 1, 2 * len);
+
+ __acl_strip_leading_space(&tmp_str);
+
+ /*
+ * Now it's something like:
+ * allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP";
+ */
+ if (strncasecmp(tmp_str, "allow", 5) == 0) {
+ memcpy(acestr, tmp_str, len);
+ tmp_str += 5;
+ /* gather the rights */
+ aci_rights_val = __aclp__get_aci_right (tmp_str);/* bug 389975 */
+ aci_item->aci_type |= ACI_HAS_ALLOW_RULE;
+
+ } else if (strncasecmp(tmp_str, "deny", 4) == 0) {
+ char *d_rule ="deny absolute";
+ /* Then we have to add "absolute" to the deny rule
+ ** What we are doing here is to tackle this situation.
+ **
+ ** allow -- deny -- allow
+ ** deny -- allow
+ **
+ ** by using deny absolute we force the precedence rule
+ ** i.e deny has a precedence over allow. Since there doesn't
+ ** seem to be an easy to detect the mix, forcing this
+ ** to all the deny rules will do the job.
+ */
+ __acl_strip_leading_space(&tmp_str);
+ tmp_str += 4;
+
+ /* We might have an absolute there already */
+ if ((s = strstr (tmp_str, "absolute")) != NULL) {
+ tmp_str = s;
+ tmp_str += 8;
+ }
+ /* gather the rights */
+ aci_rights_val = __aclp__get_aci_right (tmp_str);/* bug 389975 */
+ aci_item->aci_type |= ACI_HAS_DENY_RULE;
+
+ len = strlen ( d_rule );
+ memcpy (acestr, d_rule, len );
+ memcpy (acestr+len, tmp_str, strlen (tmp_str) );
+ } else {
+ /* wrong syntax */
+ aci_rights_val = -1 ;
+ }
+ if (aci_rights_val == -1 )
+ {
+ /* wrong syntax */
+ slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_acestr );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ } else
+ aci_item->aci_access |= aci_rights_val;
+
+
+ /* Normalize all the DNs in the userdn rule */
+
+ /*
+ *
+ * Here acestr starts like this:
+ * " allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP"
+ */
+
+ s = __aclp__getNextLASRule(aci_item, acestr, &end);
+ while ( s ) {
+ if ( 0 == strncmp ( s, DS_LAS_USERDNATTR, 10) ||
+ ( 0 == strncmp ( s, DS_LAS_USERATTR, 8))) {
+ /*
+ ** For userdnattr/userattr rule, the resources changes and hence
+ ** we cannot cache the result. See above for more comments.
+ */
+ aci_item->aci_elevel = ACI_ELEVEL_USERDNATTR;
+ } else if ( 0== strncmp ( s, DS_LAS_USERDN, 6)) {
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_USERDN;
+
+ /* XXXrbyrne
+ * Here we need to scan for more ldap:/// within
+ * this userdn rule type:
+ * eg. userdn = "ldap:///cn=joe,o=sun.com || ldap:///self"
+ * This is handled correctly in DS_LASUserDnEval
+ * but the bug here is not setting ACI_USERDN_SELFRULE
+ * which would ensure that acl info is not cached from
+ * one resource entry to the next. (bug 558519)
+ */
+ p = strstr ( p, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8; /* for ldap:/// */
+ if( __aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+
+ /* we have a rule like userdn = "ldap:///blah". s points to blah now.
+ ** let's find if we have a SELF rule like userdn = "ldap:///self".
+ ** Since the resource changes on entry basis, we can't cache the
+ ** evalation of handle for all time. The cache result is valid
+ ** within the evaluation of that resource.
+ */
+ if (strncasecmp(p, "self", 4) == 0) {
+ aci_item->aci_ruleType |= ACI_USERDN_SELFRULE;
+ } else if ( strncasecmp(p, "anyone", 6) == 0 ) {
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN_ANYONE;
+
+ } else if ( strncasecmp(p, "all", 3) == 0 ) {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_USERDN_ALL )
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN_ALL;
+
+ } else {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_USERDN )
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN;
+ }
+
+ /* See if we have a parameterized rule */
+ __aclp_chk_paramRules ( aci_item, p, end );
+ } else if ( 0 == strncmp ( s, DS_LAS_GROUPDNATTR, 11)) {
+ /*
+ ** For groupdnattr rule, the resources changes and hence
+ ** we cannot cache the result. See above for more comments.
+ */
+ /* Find out if we have a URL type of rule */
+ if ((p= strstr (s, "ldap")) != NULL) {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDNATTR_URL )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDNATTR_URL;
+ } else if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDNATTR ) {
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDNATTR;
+ }
+ aci_item->aci_ruleType |= ACI_GROUPDNATTR_RULE;
+ } else if ( 0 == strncmp ( s, DS_LAS_GROUPDN, 7)) {
+
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_GROUPDN;
+
+ p = strstr ( s, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8;
+ if (__aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ /* check for param rules */
+ __aclp_chk_paramRules ( aci_item, p, end );
+
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDN )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDN;
+ aci_item->aci_ruleType |= ACI_GROUPDN_RULE;
+
+ } else if ( 0 == strncmp ( s, DS_LAS_ROLEDN, 6)) {
+
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_ROLEDN;
+
+ p = strstr ( s, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8;
+ if (__aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ /* check for param rules */
+ __aclp_chk_paramRules ( aci_item, p, end );
+
+ /* XXX need this for roledn ?
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDN )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDN;*/
+ aci_item->aci_ruleType |= ACI_ROLEDN_RULE;
+ }
+ s = ++end;
+ s = __aclp__getNextLASRule(aci_item, s, &end);
+ }/* while */
+
+ /* get the head of the string */
+ acestr = s_acestr;
+ len = strlen( acestr);
+ ptr = acestr +len-1;
+ while (*ptr && *ptr != '\"' && *ptr != ')' ) *ptr-- = ' ';
+ ptr++;
+ *ptr = ';';
+
+ aclutil_str_appened (&ret_str, acestr);
+ if (s_acestr) {
+ slapi_ch_free ( (void **) &s_acestr );
+ }
+ s_acestr = NULL;
+
+ if (nextACE) {
+ s = strstr (nextACE, "allow");
+ if (s == NULL) s = strstr (nextACE, "deny");
+ if (s == NULL) {
+ if (nextACE && *nextACE != '\0')
+ aclutil_str_appened (&ret_str, nextACE);
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (ret_str);
+ }
+ acestr = nextACE;
+ goto normalize_nextACERule;
+ }
+
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (ret_str);
+}
+/*
+ *
+ * acl__getNextLASRule
+ * Find the next rule.
+ *
+ * Returns:
+ * endOfCurrRule - end of current rule
+ * nextRule - start of next rule
+ */
+static char *
+__aclp__getNextLASRule (aci_t *aci_item, char *original_str , char **endOfCurrRule)
+{
+ char *newstr, *word, *next, *start, *end;
+ char *ruleStart = NULL;
+ int len, ruleLen;
+ int in_dn_expr = 0;
+
+ *endOfCurrRule = NULL;
+ end = start = NULL;
+
+ newstr = slapi_ch_strdup (original_str);
+
+ if ( (strncasecmp(newstr, "allow", 5) == 0) ||
+ (strncasecmp(newstr, "deny", 4) == 0) ) {
+ word = ldap_utf8strtok_r(newstr, ")", &next);
+ }
+ else {
+ word = ldap_utf8strtok_r(newstr, " ", &next);
+ }
+
+ /*
+ * The first word is of no interest -- skip it
+ * it's allow or deny followed by the rights (<rights>),
+ * so skip over the rights as well or it's 'and', 'or',....
+ */
+
+ while ( (word = ldap_utf8strtok_r(NULL, " ", &next)) != NULL) {
+ int got_rule = 0;
+ int ruleType = 0;
+ /*
+ ** The next word must be one of these to be considered
+ ** a valid rule.
+ ** This is making me crazy. We might have a case like
+ ** "((userdn=". strtok is returning me that word.
+ */
+ len = strlen ( word );
+ word [len] = '\0';
+
+ if ( (ruleStart= strstr(word, DS_LAS_USERDNATTR)) != NULL) {
+ ruleType |= ACI_USERDNATTR_RULE;
+ ruleLen = strlen ( DS_LAS_USERDNATTR) ;
+ } else if ( (ruleStart = strstr(word, DS_LAS_USERDN)) != NULL) {
+ ruleType = ACI_USERDN_RULE;
+ ruleLen = strlen ( DS_LAS_USERDN);
+ in_dn_expr = 1;
+ } else if ( (ruleStart = strstr(word, DS_LAS_GROUPDNATTR)) != NULL) {
+ ruleType = ACI_GROUPDNATTR_RULE;
+ ruleLen = strlen ( DS_LAS_GROUPDNATTR) ;
+ } else if ((ruleStart= strstr(word, DS_LAS_GROUPDN)) != NULL) {
+ ruleType = ACI_GROUPDN_RULE;
+ ruleLen = strlen ( DS_LAS_GROUPDN) ;
+ in_dn_expr = 1;
+ } else if ((ruleStart = strstr(word, DS_LAS_USERATTR)) != NULL) {
+ ruleType = ACI_USERATTR_RULE;
+ ruleLen = strlen ( DS_LAS_USERATTR) ;
+ } else if ((ruleStart= strstr(word, DS_LAS_ROLEDN)) != NULL) {
+ ruleType = ACI_ROLEDN_RULE;
+ ruleLen = strlen ( DS_LAS_ROLEDN);
+ in_dn_expr = 1;
+ } else if ((ruleStart= strstr(word, DS_LAS_AUTHMETHOD)) != NULL) {
+ ruleType = ACI_AUTHMETHOD_RULE;
+ ruleLen = strlen ( DS_LAS_AUTHMETHOD);
+ } else if ((ruleStart = strstr(word, ACL_ATTR_IP)) != NULL) {
+ ruleType = ACI_IP_RULE;
+ ruleLen = strlen ( ACL_ATTR_IP) ;
+ } else if ((ruleStart = strstr(word, DS_LAS_TIMEOFDAY)) != NULL) {
+ ruleType = ACI_TIMEOFDAY_RULE;
+ ruleLen = strlen ( DS_LAS_TIMEOFDAY) ;
+ } else if ((ruleStart = strstr(word, DS_LAS_DAYOFWEEK)) != NULL) {
+ ruleType = ACI_DAYOFWEEK_RULE;
+ ruleLen = strlen ( DS_LAS_DAYOFWEEK) ;
+ } else if ((ruleStart = strstr(word, ACL_ATTR_DNS)) != NULL) {
+ ruleType = ACI_DNS_RULE;
+ ruleLen = strlen ( ACL_ATTR_DNS) ;
+ }
+ /* Here, we've found a space...if we were in in_dn_expr mode
+ * and we'vve found a closure for that ie.a '"' or a ')'
+ * eg. "'ldap:///all"' or 'ldap:///all")' then exit in_dn_expr mode.
+ */
+ if ( in_dn_expr && (word[len-1] == '"' ||
+ len>1 && word[len-2] == '"' ||
+ len>2 && word[len-3] == '"')) {
+ in_dn_expr = 0;
+ }
+
+ /*
+ * ruleStart may be NULL as word could be (all) for example.
+ * this word will just be skipped--we're really waiting for
+ * userdn or groupdn or...
+ */
+
+ if ( ruleStart && ruleType ) {
+ /* Look in the current word for "=" or else look into
+ ** the next word -- if none of them are true, then this
+ ** is not the start of the rule
+ */
+ char *tmpStr = ruleStart + ruleLen;
+ if ( strchr ( tmpStr, '=') ||
+ ((word = ldap_utf8strtok_r(NULL, " ", &next) ) &&
+ word && ((strncmp ( word, "=", 1) == 0 ) ||
+ (strncmp ( word, "!=",2) ==0) ||
+ (strncmp ( word, ">", 1) == 0 ) ||
+ (strncmp ( word, "<", 1) == 0 ) ||
+ (strncmp ( word, "<", 1) == 0 ) ||
+ (strncmp ( word, "<=",2) ==0 ) ||
+ (strncmp ( word, ">=",2) ==0) ||
+ (strncmp ( word, "=>",2) ==0) ||
+ (strncmp ( word, "=<",2) ==0))
+ ) ){
+ aci_item->aci_ruleType |= ruleType;
+ got_rule = 1;
+ }
+ }
+ if ( NULL == start && got_rule ) {
+ /*
+ * We've just found a rule start--keep going though because
+ * we need to return the end of this rule too.
+ */
+ start= ruleStart;
+ got_rule = 0;
+ } else {
+ /*
+ * Here, we have a candidate for the end of the rule we've found
+ * (the start of which is currently in start).
+ * But we need to be sure it really is the end and not a
+ * "fake end" due to a keyword bbeing embeded in a dn.
+ */
+ if (word && !in_dn_expr &&
+ ((strcasecmp(word, "and") == 0) ||
+ (strcasecmp(word, "or") == 0) ||
+ (strcasecmp(word, "not") == 0) ||
+ (strcasecmp(word, ";") == 0))) {
+ /* If we have start, then it really is the end */
+ word--;
+ if (start) {
+ end = word;
+ break;
+ } else {
+ /* We found a fake end, but we've no start so keep going */
+ }
+ }
+ }
+ } /* while */
+
+
+ if ( end ) {
+ /* Found an end to the rule and it's not the last rule */
+ len = end - newstr;
+ end = original_str +len;
+ while ( (end != original_str) && *end != '\"') end--;
+ *endOfCurrRule = end;
+ len = start - newstr;
+ ruleStart = original_str + len;
+ } else {
+ /* Walked off the end of the string so it's the last rule */
+ end = original_str + strlen(original_str)-1;
+ while ( (end != original_str) && *end != '\"') end--;
+ *endOfCurrRule = end;
+ }
+ if ( start ) {
+ /* Got a rule, fixup the pointer */
+ len = start - newstr;
+ ruleStart = original_str + len;
+ }
+ slapi_ch_free ( (void **) &newstr );
+
+ /*
+ * Here, ruleStart points to the start of the next rule in original_str.
+ * end points to the end of this rule.
+ */
+
+ return ( ruleStart );
+}
+/******************************************************************************
+*
+* __aclp__dn_normalize
+*
+* Normalize the DN INPLACE. This routine is similar to slapi_dn_normalize()
+* except various small stuff at the end.
+* Normalize until the "end" and not to the end of string.
+*
+******************************************************************************/
+static char *
+__aclp__dn_normalize( char *dn , char *end)
+{
+ char *d;
+
+ if ((end - dn) < 0) {
+ return(NULL);
+ }
+
+ d = slapi_dn_normalize_to_end ( dn, end );
+
+ /* Do I have the quotes already */
+ if (*d != '\"' ) {
+ /*
+ ** We are taking care of this situation
+ ** " ") ". We need to remove the space
+ ** infront and tack it after the quote like this.
+ ** "" ) ".
+ */
+
+ *d = '\"';
+ d++;
+ while (*d && *d != '\"') *d++ = ' ';
+ *d = ' ';
+ }
+
+ return( dn );
+}
+/***************************************************************************
+* acl__get_aci_right
+*
+* Go thru the one acl text str and figure our the rights declared.
+*
+*****************************************************************************/
+static int
+__aclp__get_aci_right (char *str)
+{
+
+ char *sav_str = slapi_ch_strdup(str);
+ char *t, *tt;
+ int type = 0;
+ char *delimiter = ",";
+ char *val = NULL;
+ int aclval = 0;
+
+ t = sav_str;
+ __acl_strip_leading_space( &t );
+
+ if (*t == '(' ) {
+ if ((tt = slapi_find_matching_paren(t)) == NULL) {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ } else {
+ t++; /* skip the first character which is ( */
+ *tt = '\0';
+ }
+ } else {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ }
+ /* get the tokens separated by "," */
+ val = ldap_utf8strtok_r(t,delimiter, &tt);
+ if (val == NULL )
+ {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ }
+ while (val != NULL)
+ {
+ /* get the corresponding integer value */
+ aclval = get_acl_rights_as_int(val);
+ if (aclval == -1 )
+ {
+ type = -1;
+ break;
+ }
+ type |= aclval;
+ val = ldap_utf8strtok_r(NULL,delimiter, &tt); /* get the next token */
+ }
+
+ slapi_ch_free ( (void **) &sav_str );
+ return type;
+
+}
+
+static int get_acl_rights_as_int( char * strValue)
+{
+
+ if (strValue == NULL )
+ return -1;
+ /* First strip out the leading and trailing spaces */
+ __acl_strip_leading_space( &strValue );
+ __acl_strip_trailing_space( strValue );
+
+ /* We have to do a strcasecmp (case insensitive cmp) becuase we should return
+ only if it is exact match. */
+
+ if (strcasecmp (strValue, "read") == 0 )
+ return SLAPI_ACL_READ;
+ else if (strcasecmp (strValue, "write") == 0 )
+ return SLAPI_ACL_WRITE;
+ else if (strcasecmp (strValue, "search") == 0 )
+ return SLAPI_ACL_SEARCH;
+ else if (strcasecmp (strValue, "compare") == 0 )
+ return SLAPI_ACL_COMPARE;
+ else if (strcasecmp (strValue, "add") == 0 )
+ return SLAPI_ACL_ADD;
+ else if (strcasecmp (strValue, "delete") == 0 )
+ return SLAPI_ACL_DELETE;
+ else if (strcasecmp (strValue, "proxy") == 0 )
+ return SLAPI_ACL_PROXY;
+ else if (strcasecmp (strValue, "selfwrite") == 0 )
+ return (SLAPI_ACL_SELF | SLAPI_ACL_WRITE);
+ else if (strcasecmp (strValue, "all") == 0 )
+ return SLAPI_ACL_ALL;
+ else
+ return -1; /* error */
+}
+/***************************************************************************
+*
+* acl_access2str
+*
+* Convert the access bits into character strings.
+* Example: "read, self read"
+*
+* Input:
+*
+* int access - The access in bits
+* char **rights - rights in chars
+*
+* Returns:
+* NULL - No rights to start with
+* right - rights converted.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+char *
+acl_access2str(int access)
+{
+
+ if ( access & SLAPI_ACL_COMPARE ) {
+ return access_str_compare;
+ } else if ( access & SLAPI_ACL_SEARCH ) {
+ return access_str_search;
+ } else if ( access & SLAPI_ACL_READ ) {
+ return access_str_read;
+ } else if ( access & SLAPI_ACL_DELETE) {
+ return access_str_delete;
+ } else if ( access & SLAPI_ACL_ADD) {
+ return access_str_add;
+ } else if ( (access & SLAPI_ACL_WRITE ) && (access & SLAPI_ACL_SELF)) {
+ return access_str_selfwrite;
+ } else if (access & SLAPI_ACL_WRITE ) {
+ return access_str_write;
+ } else if (access & SLAPI_ACL_PROXY ) {
+ return access_str_proxy;
+ }
+
+ return NULL;
+}
+/***************************************************************************
+*
+* __aclp__init_targetattr
+*
+* Parse the targetattr string and create a array of attrs. This will
+* help us to do evaluation at run time little faster.
+* entry.
+* Here, also extract any target value filters.
+*
+* Input:
+* aci_t *aci -- The aci item
+* char *str -- the targetattr string
+*
+* Returns:
+* ACL_OK - everything ok
+* ACL_SYNTAX_ERROR - in case of error.
+*
+*
+***************************************************************************/
+static int
+__aclp__init_targetattr (aci_t *aci, char *attr_val)
+{
+
+ int numattr=0;
+ Targetattr **attrArray;
+ char *s, *end_attr, *str;
+ int len;
+ Targetattr *attr = NULL;
+
+ s = strchr (attr_val, '=');
+ s++;
+ __acl_strip_leading_space(&s);
+ len = strlen(s);
+ if (*s == '"' && s[len-1] == '"') {
+ s[len-1] = '\0';
+ s++;
+ }
+
+ str = s;
+ attrArray = aci->targetAttr;
+
+ if (attrArray[0] != NULL) {
+ /*
+ ** That means we are visiting more than once.
+ ** Syntax error. We have a case like: (targetattr) (targetattr)
+ */
+ return ACL_SYNTAX_ERR;
+ }
+
+ while (str != 0 && *str != 0) {
+
+ __acl_strip_leading_space(&str);
+
+ if ((end_attr = strstr(str, "||")) != NULL) {
+ /* skip the two '|' chars */
+ auto char *t = end_attr;
+ LDAP_UTF8INC(end_attr);
+ LDAP_UTF8INC(end_attr);
+ *t = 0;
+ }
+ __acl_strip_trailing_space(str);
+
+ /*
+ * Here:
+ * end_attr points to the next attribute thing.
+ *
+ * str points to the current one to be processed and it looks like this:
+ * rbyrneXXX Watchout is it OK to use : as the speperator ?
+ * cn
+ * c*n*
+ * *
+ * The attribute goes in the attrTarget list.
+ *
+ */
+
+
+ attr = (Targetattr *) slapi_ch_malloc (sizeof (Targetattr));
+ memset (attr, 0, sizeof(Targetattr));
+
+ if (strchr(str, '*')) {
+
+ /* It contains a * so it's something like * or cn* */
+ if (strcmp(str, "*" ) != 0) {
+ char line[100];
+ char *lineptr = &line[0];
+ char *newline = NULL;
+ int lenstr = 0;
+ struct slapi_filter *f = NULL;
+
+ if ((lenstr = strlen(str)) > 91) { /* 100 - 8 for "(attr =%s)" */
+ newline = slapi_ch_malloc(lenstr + 9);
+ lineptr = newline;
+ }
+
+ attr->attr_type = ACL_ATTR_FILTER;
+ sprintf (lineptr, "(attr =%s)", str);
+ f = slapi_str2filter (lineptr);
+
+ if (f == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "__aclp__init_targetattr:Unable to generate filter (%s)\n", lineptr,0,0);
+ } else {
+ attr->u.attr_filter = f;
+ }
+
+ if (newline) slapi_ch_free((void **) &newline);
+ } else {
+ attr->attr_type = ACL_ATTR_STAR;
+ attr->u.attr_str = slapi_ch_strdup (str);
+ }
+
+ } else {
+ attr->u.attr_str = slapi_ch_strdup (str);
+ attr->attr_type = ACL_ATTR_STRING;
+ }
+
+
+ /*
+ * Add the attr to the targetAttr list
+ */
+
+ attrArray[numattr] = attr;
+ numattr++;
+ if (!(numattr % ACL_INIT_ATTR_ARRAY)) {
+ aci->targetAttr = (Targetattr **) slapi_ch_realloc (
+ (void *) aci->targetAttr,
+ (numattr+ACL_INIT_ATTR_ARRAY) *
+ sizeof(Targetattr *));
+ attrArray = aci->targetAttr;
+ }
+
+
+ /* Move on to the next attribute in the list */
+ str = end_attr;
+
+ } /* while */
+
+ /* NULL teminate the list */
+ attrArray[numattr] = NULL;
+ return 0;
+}
+
+void
+acl_strcpy_special (char *d, char *s)
+{
+ for (; *s; LDAP_UTF8INC(s)) {
+ switch (*s) {
+ case '.':
+ case '\\':
+ case '[':
+ case ']':
+ case '*':
+ case '+':
+ case '^':
+ case '$':
+ *d = '\\';
+ LDAP_UTF8INC(d);
+ /* FALL */
+ default:
+ d += LDAP_UTF8COPY(d, s);
+ }
+ }
+ *d = '\0';
+}
+/***************************************************************************
+*
+* acl_verify_aci_syntax
+* verify if the aci's being added for the entry has a valid syntax or not.
+*
+* Input:
+* Slapi_Entry *e - The Slapi_Entry itself
+* char **errbuf; -- error message
+*
+* Returns:
+* -1 (ACL_ERR) - Syntax error
+* 0 - No error
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_verify_aci_syntax (Slapi_Entry *e, char **errbuf)
+{
+
+ if (e != NULL) {
+ Slapi_DN *e_sdn;
+ int rv;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ slapi_entry_attr_find (e, aci_attr_type, &attr);
+ if (! attr ) return 0;
+
+ i= slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ rv=acl_verify_syntax ( e_sdn, attrVal);
+ if ( 0 != rv ) {
+ aclutil_print_err(rv, e_sdn, attrVal, errbuf);
+ return ACL_ERR;
+ }
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ }
+ return(0);
+}
+/***************************************************************************
+*
+* acl__verify_syntax
+* Called from slapi_acl_check_mods() to verify if the new aci being
+* added/replaced has the right syntax or not.
+*
+* Input:
+* Slapi_DN *e_sdn - sdn of the entry
+* berval *bval - The berval containg the aci value
+*
+* Returns:
+* return values from acl__parse_aci()
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_verify_syntax(const Slapi_DN *e_sdn, const struct berval *bval)
+{
+ aci_t *aci_item;
+ int rv = 0;
+ char *str;
+ aci_item = acllist_get_aci_new ();
+ slapi_sdn_set_ndn_byval ( aci_item->aci_sdn, slapi_sdn_get_ndn ( e_sdn ) );
+
+ /* make a copy the the string */
+ str = slapi_ch_strdup(bval->bv_val);
+ rv = acl_parse(str, aci_item);
+
+ /* cleanup before you leave ... */
+ acllist_free_aci (aci_item);
+ slapi_ch_free ( (void **) &str );
+ return(rv);
+}
+static void
+__aclp_chk_paramRules ( aci_t *aci_item, char *start, char *end)
+{
+
+ size_t len;
+ char *str;
+ char *p, *s;
+
+
+ len = end - start;
+
+ s = str = (char *) slapi_ch_calloc(1, len + 1);
+ memcpy ( str, start, len);
+ while ( (p= strchr ( s, '$')) != NULL) {
+ p++; /* skip the $ */
+ if ( 0 == strncasecmp ( p, "dn", 2))
+ aci_item->aci_ruleType |= ACI_PARAM_DNRULE;
+ else if ( 0 == strncasecmp ( p, "attr", 4))
+ aci_item->aci_ruleType |= ACI_PARAM_ATTRRULE;
+
+ s = p;
+ }
+ slapi_ch_free ( (void **) &str );
+}
+
+/*
+ * Check for an ocurrence of a macro aci in the target.
+ * value is the normalized target string.
+ *
+ * this is something like:
+ * (target="ldap:///cn=*,ou=people,($dn),o=sun.com")
+ *
+ *
+ * returns 1 if there is a $dn present.
+ * returns 0 if not.
+ * returns -1 is syntax error.
+ * If succes then:
+ * ACI_TARGET_MACRO_DN is the type.
+ * type can also include, ACI_TARGET_PATTERN, ACI_TARGET_NOT.
+ * Also aci_item->aci_macro->match_this is set to be
+ * cn=*,ou=people,($dn),o=sun.com, to be used later.
+ *
+ * . we allow at most one ($dn) in a target.
+ * . if a "*" accurs with it, it must be the first component and at most
+ * once.
+ * . it's ok for ($dn) to occur on it's own in a target, but if it appears in
+ * a user rule, then it must be in the target.
+ *
+ *
+ *
+*/
+
+static int
+acl_check_for_target_macro( aci_t *aci_item, char *value)
+{
+
+ char *str = NULL;
+
+ str = strstr( value, ACL_TARGET_MACRO_DN_KEY);
+
+ if (str != NULL) {
+ aci_item->aci_type &= ~ACI_TARGET_DN;
+ aci_item->aci_type |= ACI_TARGET_MACRO_DN;
+ aci_item->aci_macro = (aciMacro *)slapi_ch_malloc(sizeof(aciMacro));
+ aci_item->aci_macro->match_this = slapi_ch_strdup(value);
+ aci_item->aci_macro->macro_ptr = strstr( aci_item->aci_macro->match_this,
+ ACL_TARGET_MACRO_DN_KEY);
+ return(1);
+ }
+
+ return(0);
+}
+
+/* Strip trailing spaces from str by writing '\0' into them */
+
+static void
+__acl_strip_trailing_space( char *str) {
+
+ char *ptr = NULL;
+ int len = 0;
+
+ if (*str) {
+ /* ignore trailing whitespace */
+ len = strlen(str);
+ ptr = str+len-1;
+ while(ldap_utf8isspace(ptr)){ *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+}
+
+/*
+ * Strip leading spaces by resetting str to point to the first
+ * non-space charater.
+*/
+
+static void
+__acl_strip_leading_space( char **str) {
+
+ char *tmp_ptr = NULL;
+
+ tmp_ptr = *str;
+ while ( *tmp_ptr && ldap_utf8isspace( tmp_ptr ) ) LDAP_UTF8INC(tmp_ptr);
+ *str = tmp_ptr;
+}
+
+
+/*
+ * str is a string containing an LDAP filter.
+ * Trim off enclosing quotes and enclosing
+ * superfluous brackets.
+ * The result is duped so it can be kept.
+*/
+
+static char *
+__acl_trim_filterstr( char * str ) {
+
+ char *tmpstr;
+ int len;
+ char *end;
+
+ tmpstr = str;
+
+ /* If the last char is a "," take it out */
+
+ len = strlen (tmpstr);
+ if (len>0 && (tmpstr[len-1] == ',')) {
+ tmpstr [len-1] = '\0';
+ }
+
+
+ /* Does it have quotes around it */
+ len = strlen (tmpstr);
+ if (*tmpstr == '"' && tmpstr[len-1] == '"') {
+ tmpstr [len-1] = '\0';
+ tmpstr++;
+ }
+
+ str = tmpstr;
+
+ /* If we have a filter like
+ ** (((&(...) (...)))), we need to get rid of the
+ ** multiple parens or slapi_str2filter will not
+ ** evaluate properly. Need to package like
+ ** (filter ). probably I should fix str2filter
+ ** code.
+ */
+
+ while (*tmpstr++ == '(' && *tmpstr == '(') {
+ if ((end = slapi_find_matching_paren( str )) != NULL) {
+ *end = '\0';
+ str++;
+ }
+ }
+
+ return( slapi_ch_strdup(str));
+}
+
+/*
+ * Here str points to a targetattrfilters thing which looks tlike this:
+ *
+ * targetattrfilters="add=attr1:F1 && attr2:F2 ... && attrn:Fn,
+ * del=attr1:F1 && attr2:F2... && attrn:Fn")
+ *
+ *
+ */
+
+static int __acl__init_targetattrfilters( aci_t *aci, char *input_str) {
+
+ int numattr=0;
+ char *s, *str;
+ int len;
+ char *addlistptr = NULL;
+ char *dellistptr = NULL;
+
+ if (aci->targetAttrAddFilters != NULL ||
+ aci->targetAttrDelFilters != NULL) {
+
+ /*
+ ** That means we are visiting more than once.
+ ** Syntax error.
+ ** We have a case like: (targetattrfilters) (targetattrfilters)
+ */
+
+ return ACL_SYNTAX_ERR;
+ }
+
+ /* First, skip the "targetattrfilters" */
+
+ s = strchr (input_str, '=');
+ s++; /* skip the = */
+ __acl_strip_leading_space(&s); /* skip to next significant character */
+ len = strlen(s); /* Knock off the " and trailing ) */
+ if (*s == '"' && s[len-1] == '"') {
+ s[len-1] = '\0';
+ s++; /* skip the first " */
+ } else { /* No matching quotes */
+
+ return (ACL_SYNTAX_ERR);
+ }
+
+ str = s;
+
+ /*
+ * Here str looks like add=attr1:F1...attrn:Fn,
+ * del=attr1:F1...attrn:Fn
+ *
+ * extract the add and del filter lists and process each one
+ * in turn.
+ */
+
+ s = strchr (str, '=');
+ *s = '\0';
+ s++; /* skip the = */
+ __acl_strip_leading_space(&s); /* start of the first filter list */
+
+
+ /*
+ * Now str is add or del
+ * s points to the first filter list.
+ */
+
+ if (strcmp(str, "add") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_ADD_FILTERS;
+ addlistptr = s;
+
+ /* Now isolate the first filter list. */
+ if ((str = strstr(s , "del=")) || ((str = strstr(s , "del ="))) ) {
+ str--;
+ *str = '\0';
+ *str++;
+ }
+
+
+ } else if (strcmp(str, "del") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_DEL_FILTERS;
+ dellistptr = s;
+
+ /* Now isolate the first filter list. */
+ if ((str = strstr(s , "add=")) || ((str = strstr(s , "add ="))) ) {
+ str--;
+ *str = '\0';
+ *str++;
+ }
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+
+ __acl_strip_trailing_space(s);
+
+ /*
+ * Here, we have isolated the first filter list.
+ * There may be a second one.
+ * Now, str points to the start of the
+ * string that contains the second filter list.
+ * If there is none then str is NULL.
+ */
+
+ if (str != NULL ){
+
+ __acl_strip_leading_space(&str);
+ s = strchr (str, '=');
+ *s = '\0';
+ s++;
+ __acl_strip_trailing_space(str);
+ __acl_strip_leading_space(&s);
+
+
+ /*
+ * s points to the start of the second filter list.
+ * str is add or del
+ */
+
+ if (aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ if (strcmp(str, "del") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_DEL_FILTERS;
+ dellistptr = s;
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+ } else if ( aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) {
+ if (strcmp(str, "add") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_ADD_FILTERS;
+ addlistptr = s;
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+ }
+ }
+
+ /*
+ * addlistptr points to the add filter list.
+ * dellistptr points to the del filter list.
+ * In both cases the strings have been leading and trailing space
+ * stripped.
+ * Either may be NULL.
+ */
+
+ if (process_filter_list( &aci->targetAttrAddFilters, addlistptr)
+ == ACL_SYNTAX_ERR) {
+ return( ACL_SYNTAX_ERR);
+ }
+
+ if (process_filter_list( &aci->targetAttrDelFilters, dellistptr)
+ == ACL_SYNTAX_ERR) {
+ return( ACL_SYNTAX_ERR);
+ }
+
+ return(0);
+
+}
+
+/*
+ * We have a list of filters that looks like this:
+ * attr1:F1 &&....attrn:Fn
+ *
+ * We need to put each component into a targetattrfilter component of
+ * the array.
+ *
+*/
+
+static int process_filter_list( Targetattrfilter ***input_attrFilterArray,
+ char * input_str) {
+
+ char *str, *end_attr, *tmp_attr;
+ Targetattrfilter *attrfilter = NULL;
+ int numattr=0;
+ Targetattrfilter **attrFilterArray = NULL;
+
+ str = input_str;
+
+ while (str != 0 && *str != 0) {
+
+ if ((end_attr = strstr(str, "&&")) != NULL) {
+ /* skip the two '|' chars */
+ auto char *t = end_attr;
+ LDAP_UTF8INC(end_attr);
+ LDAP_UTF8INC(end_attr);
+ *t = 0;
+ }
+ __acl_strip_trailing_space(str);
+ __acl_strip_leading_space(&str);
+
+ /*
+ * Here:
+ * end_attr points to the next attribute thing.
+ *
+ * str points to the current one to be processed and it looks like
+ * this:
+ *
+ * attr1:F1
+ *
+ */
+
+ attrfilter = (Targetattrfilter *) slapi_ch_malloc (sizeof (Targetattrfilter));
+ memset (attrfilter, 0, sizeof(Targetattrfilter));
+
+ if ((tmp_attr = strstr( str,":")) != NULL) {
+
+ if ( __acl_init_targetattrfilter( attrfilter, str ) != 0 ) {
+ slapi_ch_free((void**)&attrfilter);
+ return(ACL_SYNTAX_ERR);
+ }
+ } else {
+ slapi_ch_free((void**)&attrfilter);
+ return(ACL_SYNTAX_ERR);
+ }
+
+
+ /*
+ * Add the attrfilte to the targetAttrFilter list
+ */
+
+
+ attrFilterArray = (Targetattrfilter **) slapi_ch_realloc (
+ (void *) attrFilterArray,
+ ((numattr+1)*sizeof(Targetattrfilter *)) );
+ attrFilterArray[numattr] = attrfilter;
+ numattr++;
+
+ /* Move on to the next attribute in the list */
+ str = end_attr;
+
+ }/* while */
+
+ /* NULL terminate the list */
+
+ attrFilterArray = (Targetattrfilter **) slapi_ch_realloc (
+ (void *) attrFilterArray,
+ ((numattr+1)*sizeof(Targetattrfilter *)) );
+ attrFilterArray[numattr] = NULL;
+
+ *input_attrFilterArray = attrFilterArray;
+ return 0;
+
+}
+
+/*
+ * Take str and put it into the attrfilter component.
+ *
+ * str looks as follows: attr1:F1
+ *
+ * It has had leading and trailing space stripped.
+*/
+
+static int __acl_init_targetattrfilter( Targetattrfilter *attrfilter,
+ char *str ) {
+
+ char *tmp_ptr, *s, *filter_ptr;
+ Slapi_Filter *f = NULL;
+
+ s = str;
+
+ /* First grab the attribute name */
+
+ if ( (tmp_ptr = strstr( str, ":")) == NULL ) {
+ /* No :, syntax error */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Bad targetattrfilter %s:%s\n",
+ str,"Expecting \":\"",0);
+
+ return(ACL_SYNTAX_ERR);
+ }
+ *tmp_ptr = '\0';
+ LDAP_UTF8INC(tmp_ptr);
+
+ __acl_strip_trailing_space(s);
+
+ /* s should be the attribute name-make sure it's non-empty. */
+
+ if ( *s == '\0' ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "No attribute name in targattrfilters\n",
+ 0,0);
+ return(ACL_SYNTAX_ERR);
+ }
+
+ attrfilter->attr_str = slapi_ch_strdup (s);
+
+ /* Now grab the filter */
+
+ filter_ptr = tmp_ptr;
+ __acl_strip_leading_space(&filter_ptr);
+ __acl_strip_trailing_space(filter_ptr);
+
+ /* trim dups the string, so we need to free it later if it's not kept. */
+ tmp_ptr = __acl_trim_filterstr(filter_ptr);
+
+ if ((f = slapi_str2filter(tmp_ptr)) == NULL) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Bad targetattr filter for attribute %s:%s\n",
+ attrfilter->attr_str,tmp_ptr,0);
+ slapi_ch_free( (void **) &attrfilter->attr_str);
+ slapi_ch_free( (void **) &tmp_ptr);
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /*
+ * Here verify that the named attribute is the only one
+ * that appears in the filter.
+ */
+
+ if (acl_verify_exactly_one_attribute( attrfilter->attr_str, f) !=
+ SLAPI_FILTER_SCAN_NOMORE) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Exactly one attribute type per filter allowed in targattrfilters (%s)\n",
+ attrfilter->attr_str, 0);
+ slapi_ch_free( (void **) &attrfilter->attr_str);
+ slapi_ch_free( (void **) &tmp_ptr);
+ slapi_filter_free( f, 1 );
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /* free the tmp_ptr */
+ slapi_ch_free( (void **) &tmp_ptr);
+ attrfilter->filterStr = slapi_ch_strdup (filter_ptr);
+ attrfilter->filter = f;
+
+ return(LDAP_SUCCESS);
+}
+
+/*
+ * Returns 0 if attr_name is the only attribute name to
+ * appear in original_filter AND it appears at least once.
+ * Otherwise returns STOP_FILTER_SCAN.
+*/
+
+static int acl_verify_exactly_one_attribute( char *attr_name,
+ Slapi_Filter *original_filter) {
+ int error_code;
+
+ return( slapi_filter_apply( original_filter, type_compare,
+ (void *)attr_name, &error_code));
+
+}
+
+static int type_compare( Slapi_Filter *f, void *arg) {
+
+ /* Compare only the base names: eg cn and cn;lang-eb will be the same. */
+
+ char *t = (char *)arg;
+ char *filter_type;
+ int rc = SLAPI_FILTER_SCAN_STOP;
+
+ if (slapi_filter_get_attribute_type( f, &filter_type) == 0) {
+ t = slapi_attr_syntax_normalize(t);
+ filter_type = slapi_attr_syntax_normalize(filter_type);
+
+ if (slapi_attr_type_cmp(filter_type, t, 1) == 0) {
+ rc = SLAPI_FILTER_SCAN_CONTINUE;
+ }
+
+ slapi_ch_free( (void **)&t );
+ slapi_ch_free( (void **)&filter_type );
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/acl/aclplugin.c b/ldap/servers/plugins/acl/aclplugin.c
new file mode 100644
index 00000000..a39ef3cd
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclplugin.c
@@ -0,0 +1,355 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * There are 3 ACL PLUGIN points
+ * PREOP, POSTOP and ACL plugin
+ *
+ */
+#include "acl.h"
+#include "dirver.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+static Slapi_PluginDesc pdesc = { "acl", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "acl access check plugin" };
+char *plugin_name = ACL_PLUGIN_NAME;
+
+/* Prototypes */
+
+static int aclplugin_preop_search ( Slapi_PBlock *pb );
+static int aclplugin_preop_modify ( Slapi_PBlock *pb );
+static int aclplugin_preop_common ( Slapi_PBlock *pb );
+
+/*******************************************************************************
+ * ACL PLUGIN Architecture
+ *
+ * There are 3 registered plugins:
+ *
+ * 1) PREOP ACL Plugin
+ * The preop plugin does all the initialization. It allocate the ACL
+ * PBlock and copies stuff from the connection if it needs to.
+ *
+ * 2) POSTOP ACL Plugin
+ * The Postop plugin cleans up the ACL PBlock. It copies Back to the
+ * connection struct. The Postop bind & Unbind cleans up the
+ * ACL CBlock ( struct hanging from conn struct ).
+ *
+ * 3) ACCESSCONTROL Plugin
+ * Main module which does the access check. There are 5 entry point
+ * from this plugin
+ * a) Initilize the ACL system i.e read all the ACLs and generate the
+ * the ACL List.
+ * b) Check for ACI syntax.
+ * c) Check for normal access.
+ * d) Check for access to a mod request.
+ * e) Update the in-memory ACL List.
+ *
+ *******************************************************************************/
+
+/*******************************************************************************
+ * PREOP
+ *******************************************************************************/
+
+/* Plugin identity is passed by the server in the plugin init function and must
+ be supplied by the plugin to all internal operations it initiates
+ */
+void* g_acl_preop_plugin_identity;
+
+int
+acl_preopInit (Slapi_PBlock *pb)
+{
+ int rc = 0;
+
+ /* save plugin identity to later pass to internal operations */
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &g_acl_preop_plugin_identity);
+
+ /* Declare plugin version */
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
+
+ /* Provide descriptive information */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void*)&pdesc);
+
+ /* Register functions */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void*)aclplugin_preop_search);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void*)aclplugin_preop_search);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_DELETE_FN, (void*)aclplugin_preop_modify);
+
+#if 0
+ /*
+ * XXXmcs: In order to support access control checking from
+ * extended operations, we need a SLAPI_PLUGIN_PRE_EXTENDED_FN hook.
+ * But today no such entry point exists.
+ */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_EXTENDED_FN, (void*)aclplugin_preop_modify);
+#endif
+
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= acl_preop_Init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+/* For preop search we do two things:
+ * 1) based on the search base, we preselect the acls.
+ * 2) also get hold of a acl_pblock for use
+ */
+static int
+aclplugin_preop_search ( Slapi_PBlock *pb )
+{
+ int scope;
+ char *base = NULL;
+ int optype;
+ int isRoot;
+ int rc = 0;
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_search_start ,"ACL","");
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION_TYPE, &optype );
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+
+ if ( isRoot ) {
+ TNF_PROBE_1_DEBUG(aclplugin_preop_search_end ,"ACL","",
+ tnf_string,isroot,"");
+ return rc;
+ }
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base );
+ /* For anonymous client doing search nothing needs to be set up */
+ if ( optype == SLAPI_OPERATION_SEARCH && aclanom_is_client_anonymous ( pb ) &&
+ ! slapi_dn_issuffix( base, "cn=monitor") ) {
+ TNF_PROBE_1_DEBUG(aclplugin_preop_search_end ,"ACL","",
+ tnf_string,anon,"");
+ return rc;
+ }
+
+ if ( 0 == ( rc = aclplugin_preop_common( pb ))) {
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ acllist_init_scan ( pb, scope, base );
+ }
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_search_end ,"ACL","");
+
+ return rc;
+}
+
+/*
+ * For rest of the opertion type, we get a hold of the acl
+ * private Block.
+ */
+static int
+aclplugin_preop_modify ( Slapi_PBlock *pb )
+{
+ /*
+ * Note: since we don't keep the anom profile for modifies, we have to go
+ * through the regular process to check the access.
+ */
+ return aclplugin_preop_common( pb );
+}
+
+/*
+ * Common function that is called by aclplugin_preop_search() and
+ * aclplugin_preop_modify().
+ *
+ * Return values:
+ * 0 - all is well; proceed.
+ * 1 - fatal error; result has been sent to client.
+ */
+static int
+aclplugin_preop_common( Slapi_PBlock *pb )
+{
+ char *proxy_dn; /* id being assumed */
+ char *dn; /* proxy master */
+ char *errtext = NULL;
+ int lderr;
+ Acl_PBlock *aclpb;
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_common_start ,"ACL","");
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ /*
+ * The following mallocs memory for proxy_dn, but not the dn.
+ * The proxy_dn is the id being assumed, while dn
+ * is the "proxy master".
+ */
+ proxy_dn = NULL;
+ if ( LDAP_SUCCESS != ( lderr = acl_get_proxyauth_dn( pb, &proxy_dn,
+ &errtext ))) {
+ /*
+ * Fatal error -- send a result to the client and arrange to skip
+ * any further processing.
+ */
+ slapi_send_ldap_result( pb, lderr, NULL, errtext, 0, NULL );
+ TNF_PROBE_1_DEBUG(aclplugin_preop_common_end ,"ACL","",
+ tnf_string,proxid_error,"");
+
+ return 1; /* skip any further processing */
+ }
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &dn );
+
+
+ /*
+ * The dn is copied into the aclpb during initialization.
+ */
+ if ( proxy_dn) {
+ TNF_PROBE_0_DEBUG(proxyacpb_init_start,"ACL","");
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "proxied authorization dn is (%s)\n", proxy_dn );
+ acl_init_aclpb ( pb, aclpb, proxy_dn, 1 );
+ aclpb = acl_new_proxy_aclpb (pb );
+ acl_init_aclpb ( pb, aclpb, dn, 0 );
+ slapi_ch_free ( (void **) &proxy_dn );
+
+ TNF_PROBE_0_DEBUG(proxyacpb_init_end,"ACL","");
+
+ } else {
+ TNF_PROBE_0_DEBUG(aclpb_init_start,"ACL","");
+ acl_init_aclpb ( pb, aclpb, dn, 1 );
+ TNF_PROBE_0_DEBUG(aclpb_init_end,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_common_end ,"ACL","");
+
+ return 0;
+}
+
+/*******************************************************************************
+ * POSTOP
+ *******************************************************************************/
+
+/*******************************************************************************
+ * ACCESSCONTROL PLUGIN
+ *******************************************************************************/
+
+void* g_acl_plugin_identity;
+
+/* For now, the acl component is implemented as 2 different plugins */
+/* Return the right plugin identity */
+void * aclplugin_get_identity(int plug) {
+ if (plug == ACL_PLUGIN_IDENTITY)
+ return g_acl_plugin_identity;
+ if (plug == ACL_PREOP_PLUGIN_IDENTITY)
+ return g_acl_preop_plugin_identity;
+ return NULL;
+}
+
+int
+aclplugin_init (Slapi_PBlock *pb )
+{
+
+ int rc = 0; /* OK */
+ rc = aclinit_main ( pb );
+
+ return rc;
+
+}
+int
+aclplugin_stop ( Slapi_PBlock *pb )
+{
+ int rc = 0; /* OK */
+
+ /* nothing to be done now */
+ return rc;
+}
+
+int
+acl_init( Slapi_PBlock *pb )
+{
+ int rc =0;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> acl_init\n", 0, 0, 0 );
+
+ if ( 0 != acl_init_ext() ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the extensions\n");
+ return 1;
+ }
+
+ /* save plugin identity to later pass to internal operations */
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &g_acl_plugin_identity);
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) aclplugin_init );
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) aclplugin_stop );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_SYNTAX_CHECK,
+ (void *) acl_verify_aci_syntax );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_ALLOW_ACCESS,
+ (void *) acl_access_allowed_main );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_MODS_ALLOWED,
+ (void *) acl_check_mods );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_MODS_UPDATE,
+ (void *) acl_modified );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= acl_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+/*
+ *
+ * acl_access_allowed_main
+ * Main interface to the plugin. Calls different access check functions
+ * based on the flag.
+ *
+ *
+ * Returns:
+ * LDAP_SUCCESS -- access is granted
+ * LDAP_INSUFFICIENT_ACCESS -- access denied
+ * <other ldap error> -- ex: opererations error
+ *
+ */
+int
+acl_access_allowed_main ( Slapi_PBlock *pb, Slapi_Entry *e, char **attrs,
+ struct berval *val, int access , int flags, char **errbuf)
+{
+ int rc =0;
+ char *attr = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_main_start,"ACL","");
+
+ if (attrs && *attrs) attr = attrs[0];
+
+ if (ACLPLUGIN_ACCESS_READ_ON_ENTRY == flags)
+ rc = acl_read_access_allowed_on_entry ( pb, e, attrs, access);
+ else if ( ACLPLUGIN_ACCESS_READ_ON_ATTR == flags)
+ rc = acl_read_access_allowed_on_attr ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_READ_ON_VLV == flags)
+ rc = acl_access_allowed_disjoint_resource ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_MODRDN == flags)
+ rc = acl_access_allowed_modrdn ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS == flags)
+ rc = acl_get_effective_rights ( pb, e, attrs, val, access, errbuf );
+ else
+ rc = acl_access_allowed ( pb, e, attr, val, access);
+
+ /* generate the appropriate error message */
+ if ( ( rc != LDAP_SUCCESS ) && errbuf &&
+ ( ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS != flags ) &&
+ ( access & ( SLAPI_ACL_WRITE | SLAPI_ACL_ADD | SLAPI_ACL_DELETE ))) {
+
+ char *edn = slapi_entry_get_dn ( e );
+
+ acl_gen_err_msg(access, edn, attr, errbuf);
+ }
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_main_end,"ACL","");
+
+ return rc;
+}
+#ifdef _WIN32
+
+int *module_ldap_debug = 0;
+void plugin_init_debug_level ( int *level_ptr )
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
diff --git a/ldap/servers/plugins/acl/aclproxy.c b/ldap/servers/plugins/acl/aclproxy.c
new file mode 100644
index 00000000..9065bddc
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclproxy.c
@@ -0,0 +1,195 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+#define BEGIN do {
+#define END } while(0);
+
+/* ------------------------------------------------------------
+ * LDAPProxyAuth
+ *
+ * ProxyAuthControl ::= SEQUENCE {
+ * authorizationDN LDAPDN
+ * }
+ */
+struct LDAPProxyAuth
+{
+ char *auth_dn;
+};
+typedef struct LDAPProxyAuth LDAPProxyAuth;
+
+/*
+ * delete_LDAPProxyAuth
+ */
+static void
+delete_LDAPProxyAuth(LDAPProxyAuth *spec)
+{
+ if (!spec) return;
+
+ slapi_ch_free((void**)&spec->auth_dn);
+ slapi_ch_free((void**)&spec);
+}
+
+/*
+ * parse_LDAPProxyAuth
+ *
+ * Parse a BER encoded value into the compoents of the LDAP ProxyAuth control.
+ * The 'version' parameter should be 1 or 2.
+ *
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well) and sets
+ * *errtextp if appropriate.
+ */
+static int
+parse_LDAPProxyAuth(struct berval *spec_ber, int version, char **errtextp,
+ LDAPProxyAuth **out)
+{
+ int lderr = LDAP_OPERATIONS_ERROR; /* pessimistic */
+ LDAPProxyAuth *spec = NULL;
+ BerElement *ber = NULL;
+ char *errstring = "unable to parse proxied authorization control";
+
+
+ BEGIN
+ unsigned long tag;
+
+ if ( version != 1 && version != 2 ) {
+ break;
+ }
+
+ /* create_LDAPProxyAuth */
+ spec = (LDAPProxyAuth*)slapi_ch_calloc(1,sizeof (LDAPProxyAuth));
+ if (!spec) {
+ break;
+ }
+
+ ber = ber_init(spec_ber);
+ if (!ber) {
+ break;
+ }
+
+ if ( version == 1 ) {
+ tag = ber_scanf(ber, "{a}", &spec->auth_dn);
+ } else {
+ tag = ber_scanf(ber, "a", &spec->auth_dn);
+ }
+ if (tag == LBER_ERROR) {
+ lderr = LDAP_PROTOCOL_ERROR;
+ break;
+ }
+
+ /*
+ * In version 2 of the control, the control value is actually an
+ * authorization ID (see section 9 of RFC 2829). We only support
+ * the "dnAuthzId" flavor, which looks like "dn:<DN>" where <DN> is
+ * an actual DN, e.g., "dn:uid=bjensen,dc=example,dc=com". So we
+ * need to strip off the dn: if present and reject the operation if
+ * not.
+ */
+ if (2 == version) {
+ if ( NULL == spec->auth_dn || strlen( spec->auth_dn ) < 3 ||
+ strncmp( "dn:", spec->auth_dn, 3 ) != 0 ) {
+ lderr = LDAP_INSUFFICIENT_ACCESS; /* per Proxied Auth. I-D */
+ errstring = "proxied authorization id must be a DN (dn:...)";
+ break;
+ }
+ strcpy( spec->auth_dn, spec->auth_dn + 3 );
+ }
+
+ slapi_dn_normalize(spec->auth_dn);
+ lderr = LDAP_SUCCESS; /* got it! */
+ END
+
+ /* Cleanup */
+ if (ber) ber_free(ber, 0);
+
+ if ( LDAP_SUCCESS != lderr)
+ {
+ if (spec) delete_LDAPProxyAuth(spec);
+ spec = 0;
+ if ( NULL != errtextp ) {
+ *errtextp = errstring;
+ }
+ }
+
+ *out = spec;
+
+ return lderr;
+}
+
+/*
+ * proxyauth_dn - find the users DN in the proxyauth control if it is
+ * present. The return value has been malloced for you.
+ *
+ * Returns an LDAP error code. If anything than LDAP_SUCCESS is returned,
+ * the error should be returned to the client. LDAP_SUCCESS is always
+ * returned if the proxy auth control is not present or not critical.
+ */
+int
+acl_get_proxyauth_dn( Slapi_PBlock *pb, char **proxydnp, char **errtextp )
+{
+ char *dn = 0;
+ LDAPProxyAuth *spec = 0;
+ int rv, lderr = LDAP_SUCCESS; /* optimistic */
+
+ BEGIN
+ struct berval *spec_ber;
+ LDAPControl **controls;
+ int present;
+ int critical;
+ int version = 1;
+
+ rv = slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls );
+ if (rv) break;
+
+ present = slapi_control_present( controls, LDAP_CONTROL_PROXYAUTH,
+ &spec_ber, &critical );
+ if (!present) {
+ present = slapi_control_present( controls, LDAP_CONTROL_PROXIEDAUTH,
+ &spec_ber, &critical );
+ if (!present) break;
+ version = 2;
+ /*
+ * Note the according to the Proxied Authorization I-D, the
+ * control is always supposed to be marked critical by the
+ * client. If it is not, we return a protocolError.
+ */
+ if ( !critical ) {
+ lderr = LDAP_PROTOCOL_ERROR;
+ if ( NULL != errtextp ) {
+ *errtextp = "proxy control must be marked critical";
+ }
+ break;
+ }
+ }
+
+ rv = parse_LDAPProxyAuth(spec_ber, version, errtextp, &spec);
+ if (LDAP_SUCCESS != rv) {
+ if ( critical ) {
+ lderr = rv;
+ }
+ break;
+ }
+
+ dn = slapi_ch_strdup(spec->auth_dn);
+ if (slapi_dn_isroot(dn) ) {
+ lderr = LDAP_UNWILLING_TO_PERFORM;
+ *errtextp = "Proxy dn should not be rootdn";
+ break;
+
+ }
+ END
+
+ if (spec) delete_LDAPProxyAuth(spec);
+
+ if ( NULL != proxydnp ) {
+ *proxydnp = dn;
+ } else {
+ slapi_ch_free( (void **)&dn );
+ }
+
+ return lderr;
+}
+
diff --git a/ldap/servers/plugins/acl/aclutil.c b/ldap/servers/plugins/acl/aclutil.c
new file mode 100644
index 00000000..168ca482
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclutil.c
@@ -0,0 +1,1475 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/**************************************************************************
+* Defines and usefuls stuff
+****************************************************************************/
+
+/*************************************************************************
+* Prototypes
+*************************************************************************/
+static void aclutil__typestr (int type , char str[]);
+static void aclutil__Ruletypestr (int type , char str[]);
+static char* __aclutil_extract_dn_component ( char **e_dns, int position,
+ char *attrName );
+static char* acl_get_final_component(char *macro_prefix) ;
+static char* acl_match_component( char *start, char *component);
+static int aclutil_compare_components( char * comp1, char *comp2);
+static int acl_find_comp_start(char * s, int pos );
+static PRIntn acl_ht_free_entry_and_value(PLHashEntry *he, PRIntn i,
+ void *arg);
+static PLHashNumber acl_ht_hash( const void *key);
+static PRIntn acl_ht_display_entry(PLHashEntry *he, PRIntn i, void *arg);
+
+/***************************************************************************/
+/* UTILITY FUNCTIONS */
+/***************************************************************************/
+int
+aclutil_str_appened(char **str1, const char *str2)
+{
+ int new_len;
+
+ if ( str1 == NULL || str2 == NULL )
+ return(0);
+
+ if ( *str1 == NULL ) {
+ new_len = strlen(str2) + 1;
+ *str1 = (char *)slapi_ch_malloc(new_len);
+ *str1[0] = 0;
+ } else {
+ new_len = strlen(*str1) + strlen(str2) + 1;
+ *str1 = (char *)slapi_ch_realloc(*str1, new_len);
+ }
+ if ( *str1 == NULL )
+ return(-1);
+
+ strcat(*str1, str2);
+ return(0);
+}
+
+/***************************************************************************/
+/* Print routines */
+/***************************************************************************/
+
+/* print eroror message returned from the ACL Library */
+#define ACLUTIL_ACLLIB_MSGBUF_LEN 200
+void
+acl_print_acllib_err (NSErr_t *errp , char * str)
+{
+ char msgbuf[ ACLUTIL_ACLLIB_MSGBUF_LEN ];
+
+ if ((NULL == errp ) || !slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ aclErrorFmt(errp, msgbuf, ACLUTIL_ACLLIB_MSGBUF_LEN, 1);
+ msgbuf[ACLUTIL_ACLLIB_MSGBUF_LEN-1] = '\0';
+
+ if (msgbuf)
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,"ACL LIB ERR:(%s)(%s)\n",
+ msgbuf, str ? str: "NULL",0);
+}
+void
+aclutil_print_aci (aci_t *aci_item, char *type)
+{
+ char str[BUFSIZ];
+ const char *dn;
+
+ if ( ! slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ if (!aci_item) {
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl__print_aci: Null item\n",0,0,0);
+ return;
+ }
+
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "***BEGIN ACL INFO[ Name:%s]***\n", aci_item->aclName);
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACL Index:%d ACL_ELEVEL:%d\n", aci_item->aci_index, aci_item->aci_elevel );
+ aclutil__access_str (aci_item->aci_access, str);
+ aclutil__typestr (aci_item->aci_type, &str[strlen(str)]);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACI type:(%s)\n", str);
+
+ aclutil__Ruletypestr (aci_item->aci_ruleType, str);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACI RULE type:(%s)\n",str);
+
+ dn = slapi_sdn_get_dn ( aci_item->aci_sdn );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Slapi_Entry DN:%s\n", escape_string_with_punctuation (dn, str));
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "***END ACL INFO*****************************\n");
+
+}
+void
+aclutil_print_err (int rv , const Slapi_DN *sdn, const struct berval* val,
+ char **errbuf)
+{
+ char ebuf [BUFSIZ];
+ /*
+ * The maximum size of line is ebuf_size + the log message
+ * itself (less than 200 characters for all but potentially ACL_INVALID_TARGET)
+ */
+ char line [BUFSIZ + 200];
+ char str [1024];
+ const char *dn;
+ char *lineptr = line;
+ char *newline = NULL;
+
+ if ( rv >= 0)
+ return;
+
+ if (val->bv_len > 0 && val->bv_val != NULL) {
+ sprintf (str, "%.1023s", val->bv_val);
+ } else {
+ str[0] = '\0';
+ }
+
+ dn = slapi_sdn_get_dn ( sdn );
+ if (dn && (rv == ACL_INVALID_TARGET) && ((strlen(dn) + strlen(str)) > BUFSIZ)) {
+ /*
+ * if (str_length + dn_length + 200 char message) > (BUFSIZ + 200) line
+ * we have to make space for a bigger line...
+ */
+ newline = slapi_ch_malloc(strlen(dn) + strlen(str) + 200);
+ lineptr = newline;
+ }
+
+ switch (rv) {
+ case ACL_TARGET_FILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the target filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_TARGETATTR_FILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the targetattr filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_TARGETFILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the targetfilter filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_SYNTAX_ERR:
+ sprintf (line, "ACL Syntax Error(%d):%s\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_ONEACL_TEXT_ERR:
+ sprintf (line, "ACL Syntax Error in the Bind Rules(%d):%s\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_ERR_CONCAT_HANDLES:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in Concatenating List handles\n",
+ rv);
+ break;
+ case ACL_INVALID_TARGET:
+ sprintf (lineptr, "ACL Invalid Target Error(%d): "
+ "Target is beyond the scope of the ACL(SCOPE:%s)",
+ rv, dn ? escape_string_with_punctuation (dn, ebuf) : "NULL");
+ sprintf (lineptr + strlen(lineptr), " %s\n", escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INVALID_AUTHMETHOD:
+ sprintf (line, "ACL Multiple auth method Error(%d):"
+ "Multiple Authentication Metod in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INVALID_AUTHORIZATION:
+ sprintf (line, "ACL Syntax Error(%d):"
+ "Invalid Authorization statement in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INCORRECT_ACI_VERSION:
+ sprintf (line, "ACL Syntax Error(%d):"
+ "Incorrect version Number in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ default:
+ sprintf (line, "ACL Internal Error(%d):"
+ "ACL generic error (%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ }
+
+ if (errbuf) {
+ /* If a buffer is provided, then copy the error */
+ aclutil_str_appened(errbuf, lineptr );
+ }
+
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "%s", lineptr);
+ if (newline) slapi_ch_free((void **) &newline);
+}
+
+/***************************************************************************
+* Convert access to str
+***************************************************************************/
+char*
+aclutil__access_str (int type , char str[])
+{
+ char *p;
+
+ str[0] = '\0';
+ p = str;
+
+ if (type & SLAPI_ACL_COMPARE) {
+ strcpy (p, "compare ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_SEARCH) {
+ strcpy (p, "search ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_READ) {
+ strcpy (p, "read ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_WRITE) {
+ strcpy (p, "write ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_DELETE) {
+ strcpy (p, "delete ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_ADD) {
+ strcpy (p, "add ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_SELF) {
+ strcpy (p, "self ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_PROXY) {
+ strcpy (p, "proxy ");
+ }
+ return str;
+}
+
+/***************************************************************************
+* Convert type to str
+***************************************************************************/
+static void
+aclutil__typestr (int type , char str[])
+{
+ char *p;
+
+ /* Start copying in at whatever location is passed in */
+
+ p = str;
+
+ if (type & ACI_TARGET_DN) {
+ strcpy (p, "target_DN ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_ATTR) {
+ strcpy (p, "target_attr ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_PATTERN) {
+ strcpy (p, "target_patt ");
+ p = strchr (p, '\0');
+ }
+ if ((type & ACI_TARGET_ATTR_ADD_FILTERS) | (type & ACI_TARGET_ATTR_DEL_FILTERS)) {
+ strcpy (p, "targetattrfilters ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_FILTER) {
+ strcpy (p, "target_filter ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_ACLTXT) {
+ strcpy (p, "acltxt ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_NOT) {
+ strcpy (p, "target_not ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_ATTR_NOT) {
+ strcpy (p, "target_attr_not ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_FILTER_NOT) {
+ strcpy (p, "target_filter_not ");
+ p = strchr (p, '\0');
+ }
+
+ if (type & ACI_HAS_ALLOW_RULE) {
+ strcpy (p, "allow_rule ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_HAS_DENY_RULE) {
+ strcpy (p, "deny_rule ");
+ p = strchr (p, '\0');
+ }
+}
+static void
+aclutil__Ruletypestr (int type , char str[])
+{
+ char *p;
+
+ str[0] = '\0';
+ p = str;
+ if ( type & ACI_USERDN_RULE) {
+ strcpy (p, "userdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_USERDNATTR_RULE) {
+ strcpy (p, "userdnattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_USERATTR_RULE) {
+ strcpy (p, "userattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_GROUPDN_RULE) {
+ strcpy (p, "groupdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_GROUPDNATTR_RULE) {
+ strcpy (p, "groupdnattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_ROLEDN_RULE) {
+ strcpy (p, "roledn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_IP_RULE) {
+ strcpy (p, "ip ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_DNS_RULE) {
+ strcpy (p, "dns ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_TIMEOFDAY_RULE) {
+ strcpy (p, "timeofday ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_DAYOFWEEK_RULE) {
+ strcpy (p, "dayofweek ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_AUTHMETHOD_RULE) {
+ strcpy (p, "authmethod ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_PARAM_DNRULE) {
+ strcpy (p, "paramdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_PARAM_ATTRRULE) {
+ strcpy (p, "paramAttr ");
+ p = strchr (p, '\0');
+ }
+}
+/*
+** acl_gen_err_msg
+** This function is called by backend to generate the error message
+** if access is denied.
+*/
+void
+acl_gen_err_msg(int access, char *edn, char *attr, char **errbuf)
+{
+ char *line = NULL;
+
+ if (access & SLAPI_ACL_WRITE) {
+ line = PR_smprintf(
+ "Insufficient 'write' privilege to the '%s' attribute of entry '%s'.\n",
+ attr ? attr: "NULL", edn);
+ } else if ( access & SLAPI_ACL_ADD ) {
+ line = PR_smprintf(
+ "Insufficient 'add' privilege to add the entry '%s'.\n",edn);
+
+ } else if ( access & SLAPI_ACL_DELETE ) {
+ line = PR_smprintf(
+ "Insufficient 'delete' privilege to delete the entry '%s'.\n",edn);
+ }
+ aclutil_str_appened(errbuf, line );
+
+ if (line) {
+ PR_smprintf_free(line);
+ line = NULL;
+ }
+}
+short
+aclutil_gen_signature ( short c_signature )
+{
+ short o_signature;
+ o_signature = c_signature ^ (slapi_rand() % 32768);
+ if (!o_signature)
+ o_signature = c_signature ^ (slapi_rand() % 32768);
+
+ return o_signature;
+}
+
+void
+aclutil_print_resource( struct acl_pblock *aclpb, char *right , char *attr, char *clientdn )
+{
+
+ char str[BUFSIZ];
+ const char *dn;
+
+
+ if ( aclpb == NULL) return;
+
+ if ( ! slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ************ RESOURCE INFO STARTS *********\n",0,0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " Client DN: %s\n",
+ clientdn ? escape_string_with_punctuation (clientdn, str) : "NULL", 0,0);
+ aclutil__access_str (aclpb->aclpb_access, str);
+ aclutil__typestr (aclpb->aclpb_res_type, &str[strlen(str)]);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " resource type:%d(%s)\n",
+ aclpb->aclpb_res_type, str, 0);
+
+ dn = slapi_sdn_get_dn ( aclpb->aclpb_curr_entry_sdn );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " Slapi_Entry DN: %s\n",
+ dn ? escape_string_with_punctuation ( dn , str) : "NULL",0,0);
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ATTR: %s\n", attr ? attr : "NULL",0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " rights:%s\n", right ? right: "NULL",0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ************ RESOURCE INFO ENDS *********\n",0,0,0);
+}
+/*
+ * The input string contains a rule like
+ * "cn=helpdesk, ou=$attr.deptName, o=$dn.o, o=ISP"
+ *
+ * Where $attr -- means look into the attribute list for values
+ * $dn -- means look into the entry's dn
+ *
+ * We extract the values from the entry and returned a string
+ * with the values added.
+ * For "$attr" rule - if we find multiple values then it is
+ * the pattern is not expanded.
+ * For "$dn" rule, if we find multiple of them, we use the relative
+ * position.
+ * NOTE: The caller is responsible in freeing the memory.
+ */
+char *
+aclutil_expand_paramString ( char *str, Slapi_Entry *e )
+{
+
+ char **e_dns;
+ char **a_dns;
+ char *attrName;
+ char *s, *p;
+ char *attrVal;
+ int i, len;
+ int ncomponents, type;
+ int rc = -1;
+ char *buf = NULL;
+
+
+ e_dns = ldap_explode_dn ( slapi_entry_get_ndn ( e ), 0 );
+ a_dns = ldap_explode_dn ( str, 0 );
+
+ i = 0;
+ ncomponents = 0;
+ while ( a_dns[ncomponents] )
+ ncomponents++;
+
+
+ for (i=0; i < ncomponents; i++ ) {
+
+ /* Look for"$" char */
+ if ( (s = strchr ( a_dns[i], '$') ) != NULL) {
+ p = s;
+ s++;
+ if ( strncasecmp (s, "dn", 2) == 0 )
+ type = 1;
+ else if ( strncasecmp (s, "attr", 4) == 0 )
+ type = 2;
+ else {
+ /* error */
+ goto cleanup;
+ }
+ *p = '\0';
+ aclutil_str_appened ( &buf,a_dns[i]);
+
+ if ( type == 1 ) {
+ /* xyz = $dn.o */
+ s +=3;
+ attrName = s;
+
+ attrVal = __aclutil_extract_dn_component (e_dns,
+ ncomponents-i, attrName);
+ if ( NULL == attrVal ) /*error*/
+ goto cleanup;
+
+ } else {
+ Slapi_Attr *attr;
+ const struct berval *attrValue;
+ int kk;
+ Slapi_Value *sval, *t_sval;
+
+
+ /* The pattern is x=$attr.o" */
+ s +=5;
+ attrName = s;
+
+ slapi_entry_attr_find ( e, attrName, &attr );
+ if ( NULL == attr )
+ goto cleanup;
+
+ kk= slapi_attr_first_value ( attr, &sval );
+ if ( kk != -1 ) {
+ t_sval = sval;
+ kk= slapi_attr_next_value( attr, kk, &sval );
+ if ( kk != -1 ) /* can't handle multiple --error */
+ goto cleanup;
+ }
+ attrValue = slapi_value_get_berval ( t_sval );
+ attrVal = attrValue->bv_val;
+ }
+ } else {
+ attrVal = a_dns[i];
+ }
+ aclutil_str_appened ( &buf, attrVal);
+ aclutil_str_appened ( &buf, ",");
+ }
+ rc = 0; /* everything is okay*/
+ /* remove the last comma */
+ len = strlen ( buf);
+ buf[len-1] = '\0';
+
+cleanup:
+
+ ldap_value_free ( a_dns );
+ ldap_value_free ( e_dns );
+ if ( 0 != rc ) /* error */ {
+ slapi_ch_free ( (void **) &buf );
+ buf = NULL;
+ }
+
+ return buf;
+}
+static char *
+__aclutil_extract_dn_component ( char **e_dns, int position, char *attrName )
+{
+
+ int i, matched, len;
+ char *s;
+ int matchedPosition;
+
+ len = strlen ( attrName );
+
+ /* First check if there thare are multiple of these */
+ i = matched = 0;
+ while ( e_dns[i] ) {
+ if (0 == strncasecmp (e_dns[i], attrName, len) ) {
+ matched++;
+ matchedPosition = i;
+ }
+ i++;
+ }
+
+ if (!matched )
+ return NULL;
+
+ if ( matched > 1 ) {
+ matchedPosition = i - position;
+ }
+
+ if ( NULL == e_dns[matchedPosition])
+ return NULL;
+
+ s = strstr ( e_dns[matchedPosition], "=");
+ if ( NULL == s)
+ return NULL;
+ else
+ return s+1;
+}
+
+/*
+ * Does the first component of ndn match the first component of match_this ?
+*/
+
+int
+acl_dn_component_match( const char *ndn, char *match_this, int component_number) {
+
+ return(1);
+}
+
+/*
+ * Here, ndn is a resource dn and match_this is a dn, containing a macro, ($dn).
+ *
+ * eg. ndn is cn=fred,ou=groups,ou=people,ou=icnc,o=ISP and
+ * match_this is "ou=Groups,($dn),o=ISP" or
+ * "cn=*,ou=Groups,($dn),o=ISP".
+ *
+ * They match if:
+ * match_this is a suffix of ndn
+ *
+ * It returns NULL, if they do not match.
+ * Otherwise it returns a copy of the substring of ndn that matches the ($dn).
+ *
+ * eg. in the above example, "ou=people,ou=icnc"
+*/
+
+char *
+acl_match_macro_in_target( const char *ndn, char * match_this,
+ char *macro_ptr) {
+
+ char *macro_prefix = NULL;
+ int macro_prefix_len = 0;
+ char *macro_suffix = NULL;
+ char *tmp_ptr = NULL;
+ char *matched_val = NULL;
+ char *ndn_suffix_start = NULL;
+ char *macro_prefix_final_component = NULL;
+ char *ret_val = NULL;
+ int ndn_len = 0;
+ int macro_suffix_len = 0;
+ int ndn_prefix_len = 0;
+ int ndn_prefix_end = 0;
+ int matched_val_len = 0;
+
+ /*
+ * First, grab the macro_suffix--the bit after the ($dn)
+ *
+ */
+
+ if (strlen(macro_ptr) == strlen(ACL_TARGET_MACRO_DN_KEY)) {
+ macro_suffix = NULL; /* just ($dn) */
+ } else {
+ if ( macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY)] == ',') {
+ macro_suffix = &macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY) + 1];
+ } else {
+ macro_suffix = &macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY)];
+ }
+ }
+
+ /*
+ * First ensure that the suffix of match_this is
+ * a suffix of ndn.
+ */
+
+ ndn_len = strlen(ndn);
+ if ( macro_suffix != NULL) {
+ macro_suffix_len = strlen(macro_suffix);
+ if( macro_suffix_len >= ndn_len ) {
+
+ /*
+ * eg ndn: o=icnc,o=sun.com
+ * match_this: ($dn),o=icnc,o=sun.com
+ */
+ return(NULL); /* ($dn) must match something. */
+ } else {
+ /*
+ * eg ndn: ou=People,o=icnc,o=sun.com
+ * match_this: ($dn),o=icnc,o=sun.com
+ *
+ * we can do a direct strncmp() because we know that
+ * there can be no "*" after the ($dn)...by definition.
+ */
+ if (strncasecmp( macro_suffix, &ndn[ndn_len-macro_suffix_len],
+ macro_suffix_len) != 0) {
+ return(NULL); /* suffix must match */
+ }
+ }
+ }
+
+ /* Start of the suffix in ndn...and it matched. */
+ ndn_suffix_start = (char*)&ndn[ndn_len-macro_suffix_len];
+
+ /* Here, macro_suffix is a suffix of ndn.
+ *
+ *
+ * Now, look at macro_prefix, if it is NULL, then ($dn) matches
+ * ndn[0..ndn_len-macro_suffix_len].
+ * (eg, ndn: cn=fred,ou=People,o=sun.com
+ * match_this: ($dn),o=sun.com.
+ *
+ */
+
+ macro_prefix = slapi_ch_strdup(match_this);
+
+ /* we know it's got a $(dn) */
+ tmp_ptr = strstr(macro_prefix, ACL_TARGET_MACRO_DN_KEY);
+ *tmp_ptr = '\0';
+ /* There may be a NULL prefix eg. match_this: ($dn),o=sun.com */
+ macro_prefix_len = strlen(macro_prefix);
+ if (macro_prefix_len == 0) {
+ slapi_ch_free((void **) &macro_prefix);
+ macro_prefix = NULL;
+ }
+
+ if (macro_prefix == NULL ) {
+ /*
+ * ($dn) matches ndn[0..ndn_len-macro_suffix_len]
+ */
+ int matched_val_len = 0;
+
+ matched_val_len = ndn_len-macro_suffix_len;
+
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, ndn, ndn_len-macro_suffix_len);
+ /*
+ * Null terminate matched_val, removing trailing "," if there is
+ * one.
+ */
+ if (matched_val_len > 1) {
+ if (matched_val[matched_val_len-1] == ',' ) {
+ matched_val[matched_val_len-1] = '\0';
+ } else {
+ matched_val[matched_val_len] = '\0';
+ }
+ }
+ ret_val = matched_val;
+ } else {
+
+
+ /*
+ * If it is not NULL, then if macro_prefix contains a * then
+ * it needs to be an exact prefix of ndn (modulo the * component
+ * which matches anything) becuase that's the semantics
+ * of target patterns containing *'s, except that we just
+ * make it match one component.
+ * If it is such a prefix then ($dn) matches that portion of ndn
+ * from the end of the prefix, &ndn[ndn_prefix_end] to
+ * ndn_suffix_start.
+ * If ndn_prefix_len > ndn_len-macro_suffix_len then return(NULL),
+ * otherwise $(dn) matches ndn[ndn_prefix_len..ndn_len-macro_suffix_len].
+ *
+ *
+ * eg. ndn: cn=fred,ou=P,o=sun.com
+ * match_this: cn=*,($dn),o=sun.com
+ */
+
+ if ( strstr(macro_prefix, "=*") != NULL ) {
+ int exact_match = 0;
+
+ ndn_prefix_len = acl_match_prefix( macro_prefix, ndn, &exact_match);
+ if ( ndn_prefix_len != -1 ) {
+
+ /*
+ * ndn[0..ndn_prefix_len] is the prefix in ndn.
+ * ndn[ndn_prefix_len..ndn_len-macro_suffix_len] is the
+ * matched string.
+ */
+ if (ndn_prefix_len >= ndn_len-macro_suffix_len) {
+
+ /*
+ * eg ndn: cn=fred,ou=People,o=icnc,o=sun.com
+ * cn=*,ou=People,o=icnc,($dn),o=icnc,o=sun.com
+ */
+
+ ret_val = NULL; /* matched string is empty */
+ } else {
+
+ /*
+ * eg ndn: cn=fred,ou=People,o=icnc,o=sun.com
+ * cn=*,ou=People,($dn),o=sun.com
+ */
+
+ matched_val_len = ndn_len-macro_suffix_len-ndn_prefix_len;
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, &ndn[ndn_prefix_len], matched_val_len);
+ if (matched_val_len > 1) {
+ if (matched_val[matched_val_len-1] == ',' ) {
+ matched_val[matched_val_len-1] = '\0';
+ } else {
+ matched_val[matched_val_len] = '\0';
+ }
+ }
+ matched_val[matched_val_len] = '\0';
+ ret_val = matched_val;
+ }
+ } else {
+ /* Was not a prefix so not a match */
+ ret_val = NULL;
+ }
+ } else {
+
+ /*
+ *
+ * If macro_prefix is not NULL and it does not
+ * contain a =* then
+ * we need to ensure that macro_prefix is a substring
+ * ndn.
+ * If it is and the position of the character after it's end in
+ * ndn is
+ * ndn_prefix_end then ($dn) matches
+ * ndn[ndn_prefix_end..ndn_len-macro_suffix_len].
+ *
+ *
+ * One important principal is that ($dn) matches a maximal
+ * chunk--this way it will serve to make the link
+ * between resources and users at each level of the structure.
+ *
+ * eg. ndn: ou=Groups,ou=Groups,ou=Groups,c=fr
+ * macro_prefix: ou=Groups,($dn),c=fr
+ *
+ * then ($dn) matches ou=Groups,ou=Groups.
+ *
+ *
+ *
+ * If it is not a substring, then there is no match.
+ * If it is a substring and
+ * ndn[ndn_prefix_end..ndn_len-macro_suffix_len] is empty then
+ * it's also not a match as we demand that ($dn) match a non-empty
+ * string.
+ *
+ *
+ *
+ * (eg. ndn: cn=fred,o=icnc,ou=People,o=sun.com
+ * match_this: o=icnc,($dn),o=sun.com.)
+ *
+ *
+ * (eg. ndn: cn=fred,o=menlo park,ou=People,o=icnc,o=sun.com
+ * match_this: o=menlo park,ou=People,($dn),o=sun.com
+ *
+ */
+
+ ndn_prefix_end = acl_strstr((char *)ndn, macro_prefix);
+ if ( ndn_prefix_end == -1) {
+ ret_val = NULL;
+ } else {
+ /* Is a substring */
+
+ ndn_prefix_end += macro_prefix_len;
+
+ /*
+ * make sure the matching part is non-empty:
+ *
+ * ndn[ndn_prefix_end..mndn_len-macro_suffix_len].
+ */
+
+ if ( ndn_prefix_end >= ndn_len-macro_suffix_len) {
+ ret_val = NULL;
+ } else {
+ /*
+ * ($dn) matches the non-empty string segment
+ * ndn[ndn_prefix_end..mndn_len-macro_suffix_len]
+ * the -1 is because macro_suffix_eln does not include
+ * the coma before the suffix.
+ */
+
+ matched_val_len = ndn_len-macro_suffix_len-
+ ndn_prefix_end - 1;
+
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, &ndn[ndn_prefix_end],
+ matched_val_len);
+ matched_val[matched_val_len] = '\0';
+
+ ret_val = matched_val;
+ }
+ }
+ }/* contains an =* */
+ slapi_ch_free((void **) &macro_prefix);
+ }/* macro_prefix != NULL */
+
+ return(ret_val);
+}
+
+/*
+ * Checks to see if macro_prefix is an exact prefix of ndn.
+ * macro_prefix may contain a * component.
+ *
+ * The length of the matched prefix in ndn is returned.
+ * If it was not a match, a negative int is returned.
+ * Also, if the string matched exactly,
+ * exact_match is set to 1, other wise it was a proper prefix.
+ *
+*/
+
+int
+acl_match_prefix( char *macro_prefix, const char *ndn, int *exact_match) {
+
+ int macro_index = 0;
+ int ndn_index = 0;
+ int ret_code = -1;
+ char *curr_macro_component = NULL;
+ char *curr_ndn_component = NULL;
+ int matched = 0;
+ int macro_prefix_len = 0;
+ int ndn_len = 0;
+ int i = 0;
+ int j = 0;
+ int done = 0;
+ int t = 0;
+ char * tmp_str = NULL;
+ int k,l = 0;
+
+ *exact_match = 0; /* default to not an exact match */
+
+ /* The NULL prefix matches everthing*/
+ if (macro_prefix == NULL) {
+ if ( ndn == NULL ) {
+ *exact_match = 1;
+ }
+ return(0);
+ } else {
+ /* macro_prefix is not null, so if ndn is NULL, it's not a match. */
+ if ( ndn == NULL) {
+ return(-1);
+ }
+ }
+ /*
+ * Here, neither macro_prefix nor ndn are NULL.
+ *
+ * eg. macro_prefix: cn=*,ou=people,o=sun.com
+ * ndn : cn=fred,ou=people,o=sun.com
+ */
+
+
+ /*
+ * Here, there is a component with a * (eg. cn=* ) so
+ * we need to step through the macro_prefix, and where there is
+ * such a * match on that component,
+ * when we run out of * componenets, jsut do a straight match.
+ *
+ * Out of interest, the following extended regular expression
+ * will match just one ou rdn value from a string:
+ * "^uid=admin,ou=\([^,]*\\\,\)*[^,]*,o=sun.com$"
+ *
+ *
+ * eg. cn=fred,ou=People,o=sun.com
+ *
+ *
+ * s points to the = of the component.
+ */
+
+ macro_prefix_len = strlen(macro_prefix);
+ ndn_len = strlen(ndn);
+ i = 0;
+ j = 0;
+ done = 0;
+ while ( !done ) {
+
+ /* Here ndn[0..i] has matched macro_prefix[0..j] && j<= i
+ * i<=ndn_len j<=macro_prefix_len */
+
+ if ( (t = acl_strstr(&macro_prefix[j], "=*")) < 0 ) {
+ /*
+ * No more *'s, do a straight match on
+ * macro_prefix[j..macro_prefix_len] and
+ * ndn[i..macro_prefix_len]
+ */
+
+ if( macro_prefix_len-j > ndn_len-i) {
+ /* Not a prefix, nor a match */
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /*
+ * ndn_len-i >= macro_prefix_len - j
+ * if macro_prefix_len-j is 0, then
+ * it's a null prefix, so it matches.
+ * If in addition ndn_len-i is 0 then it's
+ * an exact match.
+ * Otherwise, do the cmp.
+ */
+
+ if ( macro_prefix_len-j == 0) {
+ done = 1;
+ ret_code = i;
+ if ( ndn_len-i == 0) {
+ *exact_match = 1;
+ }
+ }else {
+
+ if (strncasecmp(&macro_prefix[j], &ndn[i],
+ macro_prefix_len-j) == 0) {
+ *exact_match = (macro_prefix_len-j == ndn_len-i);
+ ret_code = i + macro_prefix_len -j;
+ done = 1;
+ } else {
+ /* not a prefix not a match */
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ }
+ }
+ }
+ }else {
+ /*
+ * Is another * component, so:
+ * 1. match that component in macro_prefix (at pos k say)
+ * with the corresponding compoent (at pos l say ) in ndn
+ *
+ * 2. match the intervening string ndn[i..l] and
+ * macro_prefix[j..k].
+ */
+
+ /* First, find the start of the component in macro_prefix. */
+
+ t++; /* move to the--this way we will look for "ou=" in ndn */
+ k = acl_find_comp_start(macro_prefix, t);
+
+ /* Now identify that component in ndn--if it's not there no match */
+ tmp_str = slapi_ch_malloc(t-k+1);
+ strncpy(tmp_str, &macro_prefix[k], t-k);
+ tmp_str[t-k] = '\0';
+ l = acl_strstr((char*)&ndn[i], tmp_str);
+ if (l == -1) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /*
+ * Found the comp in ndn, so the comp matches.
+ * Now test the intervening string segments:
+ * ndn[i..l] and macro_prefix[j..k]
+ */
+
+ if ( k-j != l-i ) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else{
+ if (strncasecmp(&macro_prefix[j], &ndn[i], k-j) != 0) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /* Matched, so bump i and j and keep going.*/
+ i += acl_find_comp_end((char*)&ndn[l]);
+ j += acl_find_comp_end((char*)&macro_prefix[k]);
+ }
+ }
+ }
+ slapi_ch_free((void **)&tmp_str);
+ }
+ }/* while */
+
+ return(ret_code);
+
+}
+
+/*
+ * returns the index in s of where the component at position
+ * s[pos] starts.
+ * This is the index of the character after the first unescaped comma
+ * moving backwards in s from pos.
+ * If this is not found then return 0, ie. the start of the string.
+ * If the index returned is > strlen(s) then could not find it.
+ * only such case is if you pass ",", in which case there is no component start.
+*/
+
+static int
+acl_find_comp_start(char * s, int pos ) {
+
+ int i =0;
+ int comp_start = 0;
+
+ i = pos;
+ while( i > 0 && (s[i] != ',' ||
+ s[i-1] == '\\')) {
+ i--;
+ }
+ /*
+ * i == 0 || (s[i] == ',' && s[i-1] != '\\')
+ */
+ if (i==0) {
+ /* Got all the way with no unescaped comma */
+ if (s[i] == ',') {
+ comp_start = i+1;
+ } else {
+ comp_start = i;
+ }
+ } else { /* Found an unescaped comma */
+ comp_start = i + 1;
+ }
+
+ return( comp_start);
+}
+
+/*
+ * returns the index in s of the first character after the
+ * first unescaped comma.
+ * If ther is no such character, returns strlen(s);
+*/
+
+int
+acl_find_comp_end( char * s) {
+
+ int i = 0;
+ int s_len = 0;
+
+ s_len = strlen(s);
+
+ if ( s_len == 0 || s_len == 1) {
+ return(s_len);
+ }
+
+ /* inv: i+1<s_len && (s[i] == '\\' || s[i+1] != ',')*/
+
+ i = 0;
+ while( i+1 < s_len && (s[i] == '\\' ||
+ s[i+1] != ',')) {
+ i++;
+ }
+ if ( i + 1 == s_len) {
+ return(s_len);
+ } else {
+ return(i+2);
+ }
+}
+
+/*
+ * return the index in s where substr occurs, if none
+ * returns -1.
+*/
+
+int
+acl_strstr(char * s, char *substr) {
+
+ char *t = NULL;
+ char *tmp_str = NULL;
+
+ tmp_str = slapi_ch_strdup(s);
+
+ if ( (t = strstr(tmp_str, substr)) == NULL ) {
+ slapi_ch_free((void **)&tmp_str);
+ return(-1);
+ } else {
+ int l = 0;
+ *t = '\0';
+ l = strlen(tmp_str);
+ slapi_ch_free((void **)&tmp_str);
+ return(l);
+ }
+}
+
+/*
+ * replace all occurences of substr in s with replace_str.
+ *
+ * returns a malloced version of the patched string.
+*/
+
+char *
+acl_replace_str(char * s, char *substr, char* replace_with_str) {
+
+ char *str = NULL;
+ char *working_s, *suffix, *prefix, *patched;
+ int replace_with_len, substr_len, prefix_len, suffix_len;
+
+ if ( (str = strstr(s, substr)) == NULL) {
+ return(slapi_ch_strdup(s));
+ } else {
+
+
+ replace_with_len = strlen(replace_with_str);
+ substr_len = strlen(substr);
+
+ working_s = slapi_ch_strdup(s);
+ prefix = working_s;
+ str = strstr(prefix, substr);
+
+ while (str != NULL) {
+
+ /*
+ * working_s is a copy of the string to be patched
+ * str points to a substr to be patched
+ * prefix points to working_s
+ */
+
+ *str = '\0';
+
+ suffix = &str[substr_len];
+ prefix_len = strlen(prefix);
+ suffix_len = strlen(suffix);
+
+ patched = (char *)slapi_ch_malloc(prefix_len +
+ replace_with_len +
+ suffix_len +1 );
+ strcpy(patched, prefix);
+ strcat(patched, replace_with_str);
+ strcat(patched, suffix);
+
+ slapi_ch_free((void **)&working_s);
+
+ working_s = patched;
+ prefix = working_s;
+ str = strstr(prefix, substr);
+
+ }
+
+ return(working_s);
+ }
+
+}
+
+
+/*
+ * Start at index and return a malloced string that is the
+ * next component in dn (eg. "ou=People"),
+ * or NULL if couldn't find the next one.
+*/
+
+char *
+get_next_component(char *dn, int *index) {
+
+ int dn_len = strlen(dn);
+ int start_next = -1;
+ int i = 0;
+ char *ret_comp;
+
+ if (*index>= dn_len) {
+ return(NULL);
+ }
+
+ start_next = acl_find_comp_end( &dn[*index]);
+
+ if ( start_next >= dn_len ) {
+ *index = start_next;
+ return(NULL); /* no next comp */
+ }
+
+ /*
+ *Here, start_next should be the start of the next
+ * component--so far have not run off the end.
+ */
+
+ i = acl_find_comp_end( &dn[start_next]);
+
+ /*
+ * Here, the matched string is all from start_next to i.
+ */
+
+ ret_comp = (char *)slapi_ch_malloc(i - start_next +1);
+ memcpy( ret_comp, &dn[start_next], i-start_next);
+ ret_comp[i-start_next] = '\0';
+
+ return(ret_comp);
+}
+
+char *
+get_this_component(char *dn, int *index) {
+
+ int dn_len = strlen(dn);
+ int i = 0;
+ char *ret_comp;
+
+ if (*index>= dn_len) {
+ return(NULL);
+ }
+
+ if (dn_len == *index + 1) {
+ /* Just return a copy of the string. */
+ return(slapi_ch_strdup(dn));
+ }else {
+ /* *index + 1 < dn_len */
+ i = *index+1;
+ while( (dn[i] != '\0') && dn[i] != ',' && dn[i-1] != '\\') {
+ i += 1;
+ }
+
+ /*
+ * Here, the matched string is all from *index to i.
+ */
+
+ ret_comp = (char *)slapi_ch_malloc(i - *index +1);
+ memcpy( ret_comp, &dn[*index], i - *index);
+ ret_comp[i-*index] = '\0';
+
+ if (i < dn_len) {
+ /* Found a comma before the end */
+ *index = i + 1; /* skip it */
+ }
+
+ return(ret_comp);
+ }
+
+}
+
+/*
+ * return 1 if comp1==comp2,
+ * return 0 otherwise.
+ *
+ * the components might have *'s.
+ *
+ * eg: comp1: cn=*
+ * comp2: cn=fred
+ *
+ *
+*/
+
+static int
+aclutil_compare_components( char * comp1, char *comp2) {
+
+ char *tmp_str = NULL;
+
+ tmp_str = strstr( comp1, "=*");
+ if ( tmp_str == NULL) {
+
+ /* Just a straight cmp */
+
+ if (slapi_utf8casecmp((ACLUCHP)comp1, (ACLUCHP)comp2) == 0) {
+ return(1);
+ } else {
+ return(0);
+ }
+ } else {
+
+ char *tmp_comp1= NULL;
+ char *tmp_comp2 = NULL;
+ int ret_code = 0;
+
+ /* Here, just compare the bit before the = */
+
+ tmp_comp1 = slapi_ch_strdup(comp1);
+ tmp_comp2 = slapi_ch_strdup(comp2);
+
+ /*
+ * Probably need to verify it's not escaped--see code for looking for
+ * unescaped commas.
+ */
+
+ tmp_str = strstr(tmp_comp1, "=");
+ *tmp_str = '\0';
+
+ tmp_str = strstr(tmp_comp2, "=");
+ if ( tmp_str == NULL) {
+ ret_code = 0;
+ } else{
+
+ *tmp_str = '\0';
+
+ if (slapi_utf8casecmp((ACLUCHP)comp1, (ACLUCHP)comp2) == 0) {
+ ret_code = 1;
+ } else {
+ ret_code = 0;
+ }
+
+ slapi_ch_free((void **)&tmp_comp1);
+ slapi_ch_free((void **)&tmp_comp2);
+
+ return(ret_code);
+
+ }
+
+ }
+}
+
+/*
+ * return a pointer to the final component of macro_prefix.
+*/
+
+static char *
+acl_get_final_component(char *macro_prefix) {
+
+ return(NULL);
+}
+
+/*
+ *
+ *
+*/
+
+static char *
+acl_match_component( char *start, char *component) {
+
+
+ return(NULL);
+}
+
+/* acl hash table funcs */
+
+/*
+ * Add the key adn value to the ht.
+ * If it already exists then remove the old one and free
+ * the value.
+*/
+void acl_ht_add_and_freeOld(acl_ht_t * acl_ht,
+ PLHashNumber key,
+ char *value){
+ char *old_value = NULL;
+
+ if ( (old_value = (char *)acl_ht_lookup( acl_ht, key)) != NULL ) {
+ acl_ht_remove( acl_ht, key);
+ slapi_ch_free((void **)&old_value);
+ }
+
+ PL_HashTableAdd( acl_ht, (const void *)key, value);
+}
+
+/*
+ * Return a new acl_ht_t *
+*/
+acl_ht_t *acl_ht_new(void) {
+
+ return(PL_NewHashTable(30, acl_ht_hash, /* key hasher */
+ PL_CompareValues, /* keyCompare */
+ PL_CompareStrings, 0, 0)); /* value compare */
+}
+
+static PLHashNumber acl_ht_hash( const void *key) {
+
+ return( (PLHashNumber)key );
+}
+
+/* Free all the values in the ht */
+void acl_ht_free_all_entries_and_values( acl_ht_t *acl_ht) {
+
+ PL_HashTableEnumerateEntries( acl_ht, acl_ht_free_entry_and_value,
+ NULL);
+}
+
+static PRIntn
+acl_ht_free_entry_and_value(PLHashEntry *he, PRIntn i, void *arg)
+{
+
+ slapi_ch_free((void **)&he->value); /* free value */
+
+ /* Free this entry anfd go on to next one */
+ return ( HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE);
+}
+
+/* Free all the values in the ht */
+void acl_ht_display_ht( acl_ht_t *acl_ht) {
+
+#ifdef DEBUG
+ PL_HashTableEnumerateEntries( acl_ht, acl_ht_display_entry, NULL);
+#endif
+}
+
+static PRIntn
+acl_ht_display_entry(PLHashEntry *he, PRIntn i, void *arg)
+{
+ PLHashNumber aci_index = (PLHashNumber)he->key;
+ char *matched_val = (char *)he->value;
+
+ LDAPDebug(LDAP_DEBUG_ACL,"macro ht entry: key='%d' matched_val='%s'"
+ "keyhash='%d'\n",
+ aci_index, (matched_val ? matched_val: "NULL"),
+ (PLHashNumber)he->keyHash);
+
+ return HT_ENUMERATE_NEXT;
+
+}
+
+/* remove this entry from the ht--doesn't free the value.*/
+void acl_ht_remove( acl_ht_t *acl_ht, PLHashNumber key) {
+
+ PL_HashTableRemove( acl_ht, (const void *)key);
+}
+
+/* Retrieve a pointer to the value of the entry with key */
+void *acl_ht_lookup( acl_ht_t *acl_ht,
+ PLHashNumber key) {
+
+ return( PL_HashTableLookup( acl_ht, (const void *)key) );
+}
+
+
+/***************************************************************************/
+/* E N D */
+/***************************************************************************/
+
diff --git a/ldap/servers/plugins/acl/libacl.def b/ldap/servers/plugins/acl/libacl.def
new file mode 100644
index 00000000..947638cd
--- /dev/null
+++ b/ldap/servers/plugins/acl/libacl.def
@@ -0,0 +1,16 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7.0 ACL Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ acl_preopInit @1
+; unused @2
+ acl_init @3
+ plugin_init_debug_level @4