summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/acl/aclutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/acl/aclutil.c')
-rw-r--r--ldap/servers/plugins/acl/aclutil.c1475
1 files changed, 1475 insertions, 0 deletions
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 */
+/***************************************************************************/
+