--- /dev/null
+Copyright 2005 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+This directory contains native slapd plugins that implement access rules.
+
+posixgroup.c contains a simple example that implements access control
+based on posixGroup membership, loosely inspired by ITS#3849. It should
+be made clear that this access control policy does not reflect any
+standard track model of handling access control, and should be
+essentially viewed as an illustration of the use of the dynamic
+extension of access control within slapd.
+
+To use the acl-posixgroup plugin, add:
+
+moduleload acl-posixgroup.so
+
+to your slapd configuration file; it requires "nis.schema" to be loaded.
+It is configured using
+
+access to <what>
+ by dynacl/posixGroup[.{exact,expand}]=<dnpat> {<level>|<priv(s)}
+
+The default is "exact"; in case of "expand", "<dnpat>" results from
+the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>"
+describe the level of privilege this rule can assume.
+
+No Makefile is provided. Use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o acl-posixgroup.so posixgroup.c
+
+to compile the posixGroup ACL plugin.
+
--- /dev/null
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2005 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <slap.h>
+#include <lutil.h>
+
+/* Need dynacl... */
+
+#ifdef SLAP_DYNACL
+
+typedef struct pg_t {
+ slap_style_t pg_style;
+ struct berval pg_pat;
+} pg_t;
+
+static ObjectClass *pg_posixGroup;
+static AttributeDescription *pg_memberUid;
+static ObjectClass *pg_posixAccount;
+static AttributeDescription *pg_uidNumber;
+
+static int pg_dynacl_destroy( void *priv );
+
+static int
+pg_dynacl_parse(
+ const char *fname,
+ int lineno,
+ slap_style_t style,
+ const char *pattern,
+ void **privp )
+{
+ pg_t *pg;
+ int rc;
+ const char *text = NULL;
+ struct berval pat;
+
+ ber_str2bv( pattern, 0, 0, &pat );
+
+ pg = ch_calloc( 1, sizeof( pg_t ) );
+
+ pg->pg_style = style;
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ rc = dnNormalize( 0, NULL, NULL, &pat, &pg->pg_pat, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to normalize DN \"%s\".\n",
+ fname, lineno, pattern );
+ goto cleanup;
+ }
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ber_dupbv( &pg->pg_pat, &pat );
+ break;
+
+ default:
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unsupported style \"%s\".\n",
+ fname, lineno, style_strings[ pg->pg_style ] );
+ goto cleanup;
+ }
+
+ if ( pg_posixGroup == NULL ) {
+ pg_posixGroup = oc_find( "posixGroup" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixGroup\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ pg_posixAccount = oc_find( "posixAccount" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixAccount\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "memberUid", &pg_memberUid, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"memberUid\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "uidNumber", &pg_uidNumber, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"uidNumber\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+ }
+
+ *privp = (void *)pg;
+ return 0;
+
+cleanup:
+ (void)pg_dynacl_destroy( (void *)pg );
+
+ return 1;
+}
+
+static int
+pg_dynacl_unparse(
+ void *priv,
+ struct berval *bv )
+{
+ pg_t *pg = (pg_t *)priv;
+ char *ptr;
+
+ bv->bv_len = STRLENOF( " dynacl/posixGroup.expand=" ) + pg->pg_pat.bv_len;
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+
+ ptr = lutil_strcopy( bv->bv_val, " dynacl/posixGroup" );
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ ptr = lutil_strcopy( ptr, ".exact=" );
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ptr = lutil_strcopy( ptr, ".expand=" );
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ ptr = lutil_strncopy( ptr, pg->pg_pat.bv_val, pg->pg_pat.bv_len );
+ ptr[ 0 ] = '\0';
+
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+pg_dynacl_mask(
+ void *priv,
+ struct slap_op *op,
+ Entry *target,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ pg_t *pg = (pg_t *)priv;
+ Entry *group = NULL,
+ *user = NULL;
+ int rc;
+ Backend *be = op->o_bd,
+ *group_be = NULL,
+ *user_be = NULL;
+ struct berval group_ndn;
+
+ ACL_INVALIDATE( *deny );
+
+ /* get user */
+ if ( target && dn_match( &target->e_nname, &op->o_ndn ) ) {
+ user = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ user_be = op->o_bd = select_backend( &op->o_ndn, 0, 0 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+ rc = be_entry_get_rw( op, &op->o_ndn, pg_posixAccount, pg_uidNumber, 0, &user );
+ }
+
+ if ( rc != LDAP_SUCCESS || user == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+
+ /* get target */
+ if ( pg->pg_style == ACL_STYLE_EXPAND ) {
+ char buf[ 1024 ];
+ struct berval bv;
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ if ( acl_string_expand( &bv, &pg->pg_pat,
+ target->e_nname.bv_val,
+ nmatch, matches ) )
+ {
+ goto cleanup;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &group_ndn,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ /* did not expand to a valid dn */
+ goto cleanup;
+ }
+
+ } else {
+ group_ndn = pg->pg_pat;
+ }
+
+ if ( target && dn_match( &target->e_nname, &group_ndn ) ) {
+ group = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ group_be = op->o_bd = select_backend( &group_ndn, 0, 0 );
+ if ( op->o_bd == NULL ) {
+ goto cleanup;
+ }
+ rc = be_entry_get_rw( op, &group_ndn, pg_posixGroup, pg_memberUid, 0, &group );
+ }
+
+ if ( group_ndn.bv_val != pg->pg_pat.bv_val ) {
+ op->o_tmpfree( group_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc == LDAP_SUCCESS && group != NULL ) {
+ Attribute *a_uid,
+ *a_member;
+
+ a_uid = attr_find( user->e_attrs, pg_uidNumber);
+ if ( !a_uid || !BER_BVISNULL( &a_uid->a_nvals[ 1 ] ) ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ a_member = attr_find( group->e_attrs, pg_memberUid );
+ if ( !a_member ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ rc = value_find_ex( pg_memberUid,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a_member->a_nvals, &a_uid->a_nvals[ 0 ],
+ op->o_tmpmemctx );
+ }
+ }
+
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+
+
+ if ( rc == LDAP_SUCCESS ) {
+ ACL_LVL_ASSIGN_WRITE( *grant );
+ }
+
+cleanup:;
+ if ( group != NULL && group != target ) {
+ op->o_bd = group_be;
+ be_entry_release_r( op, group );
+ op->o_bd = be;
+ }
+
+ if ( user != NULL && user != target ) {
+ op->o_bd = user_be;
+ be_entry_release_r( op, group );
+ op->o_bd = be;
+ }
+
+ return 0;
+}
+
+static int
+pg_dynacl_destroy(
+ void *priv )
+{
+ pg_t *pg = (pg_t *)priv;
+
+ if ( pg != NULL ) {
+ if ( !BER_BVISNULL( &pg->pg_pat ) ) {
+ ber_memfree( pg->pg_pat.bv_val );
+ }
+ ch_free( pg );
+ }
+
+ return 0;
+}
+
+static struct slap_dynacl_t pg_dynacl = {
+ "posixGroup",
+ pg_dynacl_parse,
+ pg_dynacl_unparse,
+ pg_dynacl_mask,
+ pg_dynacl_destroy
+};
+
+int
+init_module( int argc, char *argv[] )
+{
+ return slap_dynacl_register( &pg_dynacl );
+}
+
+#endif /* SLAP_DYNACL */
.TP
.B syncprov-checkpoint <ops> <minutes>
After a write operation has succeeded, write the contextCSN to the underlying
-database if <ops> write operations or more than <minutes> time have passed
+database if
+.B <ops>
+write operations or more than
+.B <minutes>
+time have passed
since the last checkpoint. Checkpointing is disabled by default.
.TP
-.B syncprov-sessionlog <sid> <size>
-Specify a session log for recording information about entries that have been
-scoped out of the content identified by
-.BR <sid> .
-The number of entries in the log is limited by
-.BR <size> .
-Both
-.B <sid>
-and
-.B <size>
-must be non-negative integers, and
-.B <sid>
-can have no more than three decimal digits.
+.B syncprov-sessionlog <ops>
+Specify a session log for recording information about write operations made
+on the database. The
+.B <ops>
+specifies the number of operations that are recorded in the log. All write
+operations (except Adds) are recorded in the log.
When using the session log, it is helpful to set an eq index on the
entryUUID attribute in the underlying database.
.SH FILES
attributes.
.TP
.B unique_attributes <attribute...>
-Specify one or more attributes which for which uniqueness will be enforced.
+Specify one or more attributes for which uniqueness will be enforced.
If not specified, all attributes which are not operational (eg, system
attributes such as
.B entryUUID )
list, and included in the
.B unique_attributes
list, in that order. This makes it possible to create interesting and
-unusable configurations.
+unusable configurations. Usually only one of
+.B unique_ignore
+or
+.B unique_attributes
+should be configured; use
+.B unique_ignore
+if the majority of attributes should be unique, and use
+.B unique_attributes
+if only a small set of attributes should be unique.
.LP
Typical attributes for the
.B unique_ignore
option.
.TP
.BI \-a " filter"
-Only dump entries matching the asserted filter.
+Only dump entries matching the asserted filter.
+For example
+
+slapcat -a \\
+ "(!(entryDN:dnSubtreeMatch:=ou=People,dc=example,dc=com))"
+
+will dump all but the "ou=People,dc=example,dc=com" subtree
+of the "dc=example,dc=com" database.
.TP
.BI \-s " subtree-dn"
Only dump entries in the subtree specified by this DN.
#define SLAPI_X_OPERATION_DELETE_GLUE_PARENT 1305
#define SLAPI_X_MANAGEDIT 1306
#define SLAPI_X_OPERATION_NO_SCHEMA_CHECK 1307
+#define SLAPI_X_ADD_STRUCTURAL_CLASS 1308
/* Authentication types */
#define SLAPD_AUTH_NONE "none"
return dest;
}
-int
-ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
-{
- return ldap_url_parselist_ext( ludlist, url, ", " );
-}
-
-int
-ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep )
+static int
+ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep,
+ int (*url_parse)( const char *, LDAPURLDesc ** ) )
{
int i, rc;
LDAPURLDesc *ludp;
for (i = 0; urls[i] != NULL; i++) ;
/* ...and put them in the "stack" backward */
while (--i >= 0) {
- rc = ldap_url_parse( urls[i], &ludp );
+ rc = url_parse( urls[i], &ludp );
if ( rc != 0 ) {
ldap_charray_free(urls);
ldap_free_urllist(*ludlist);
return LDAP_URL_SUCCESS;
}
+int
+ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
+{
+ return ldap_url_parselist_int( ludlist, url, ", ", ldap_url_parse );
+}
+
+int
+ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep )
+{
+ return ldap_url_parselist_int( ludlist, url, sep, ldap_url_parse_ext );
+}
+
int
ldap_url_parsehosts(
LDAPURLDesc **ludlist,
char *lr_pattern;
char *lr_subststring;
char *lr_flagstring;
-#ifdef USE_REWRITE_LDAP_PVT_THREADS
- ldap_pvt_thread_mutex_t lr_mutex;
-#endif /* USE_REWRITE_LDAP_PVT_THREADS */
- regex_t lr_regex;
+ regex_t lr_regex;
/*
* I was thinking about some kind of per-rule mutex, but there's
return REWRITE_ERR;
}
-#ifdef USE_REWRITE_LDAP_PVT_THREADS
- if ( ldap_pvt_thread_mutex_init( &rule->lr_mutex ) ) {
- regfree( &rule->lr_regex );
- free( rule );
- return REWRITE_ERR;
- }
-#endif /* USE_REWRITE_LDAP_PVT_THREADS */
-
/*
* Just to remember them ...
*/
op->lo_num_passes++;
-#ifdef USE_REWRITE_LDAP_PVT_THREADS
- ldap_pvt_thread_mutex_lock( &rule->lr_mutex );
-#endif /* USE_REWRITE_LDAP_PVT_THREADS */
rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
-#ifdef USE_REWRITE_LDAP_PVT_THREADS
- ldap_pvt_thread_mutex_unlock( &rule->lr_mutex );
-#endif /* USE_REWRITE_LDAP_PVT_THREADS */
if ( rc != 0 ) {
if ( *result == NULL && string != arg ) {
free( string );
}
regfree( &rule->lr_regex );
-#ifdef USE_REWRITE_LDAP_PVT_THREADS
- ldap_pvt_thread_mutex_destroy( &rule->lr_mutex );
-#endif /* USE_REWRITE_LDAP_PVT_THREADS */
for ( action = rule->lr_action; action; ) {
struct rewrite_action *curraction = action;
backover.c ctxcsn.c ldapsync.c frontend.c \
slapadd.c slapcat.c slapcommon.c slapdn.c slapindex.c \
slappasswd.c slaptest.c slapauth.c slapacl.c component.c \
+ aci.c \
$(@PLAT@_SRCS)
OBJS = main.o globals.o bconfig.o config.o daemon.o \
backover.o ctxcsn.o ldapsync.o frontend.o \
slapadd.o slapcat.o slapcommon.o slapdn.o slapindex.o \
slappasswd.o slaptest.o slapauth.o slapacl.o component.o \
+ aci.o \
$(@PLAT@_OBJS)
LDAP_INCDIR= ../../include -I$(srcdir) -I$(srcdir)/slapi -I.
return SLAPD_DISCONNECT;
}
+ Statslog( LDAP_DEBUG_STATS, "%s ABANDON msg=%ld\n",
+ op->o_log_prefix, (long) id, 0, 0, 0 );
+
if( get_ctrls( op, rs, 0 ) != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "do_abandon: get_ctrls failed\n", 0, 0 ,0 );
return rs->sr_err;
--- /dev/null
+/* aci.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2005 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_ACI_ENABLED
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+#include "lutil.h"
+
+#define ACI_BUF_SIZE 1024 /* use most appropriate size */
+
+static int
+aci_list_map_rights(
+ struct berval *list )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ ACL_INIT( mask );
+ for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
+ if ( bv.bv_len <= 0 ) {
+ continue;
+ }
+
+ switch ( *bv.bv_val ) {
+ case 'c':
+ ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
+ break;
+ case 's':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+ * the right 's' to mean "set", but in the examples states
+ * that the right 's' means "search". The latter definition
+ * is used here.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
+ break;
+ case 'r':
+ ACL_PRIV_SET(mask, ACL_PRIV_READ);
+ break;
+ case 'w':
+ ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
+ break;
+ case 'x':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
+ * define any equivalent to the AUTH right, so I've just used
+ * 'x' for now.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ return mask;
+}
+
+static int
+aci_list_has_attr(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv, left, right;
+ int i;
+
+ for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
+ if ( acl_get_part(&bv, 0, '=', &left ) < 0
+ || acl_get_part( &bv, 1, '=', &right ) < 0 )
+ {
+ if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
+ return(1);
+ }
+
+ } else if ( val == NULL ) {
+ if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
+ return(1);
+ }
+
+ } else {
+ if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
+ /* FIXME: this is also totally undocumented! */
+ /* this is experimental code that implements a
+ * simple (prefix) match of the attribute value.
+ * the ACI draft does not provide for aci's that
+ * apply to specific values, but it would be
+ * nice to have. If the <attr> part of an aci's
+ * rights list is of the form <attr>=<value>,
+ * that means the aci applies only to attrs with
+ * the given value. Furthermore, if the attr is
+ * of the form <attr>=<value>*, then <value> is
+ * treated as a prefix, and the aci applies to
+ * any value with that prefix.
+ *
+ * Ideally, this would allow r.e. matches.
+ */
+ if ( acl_get_part( &right, 0, '*', &left ) < 0
+ || right.bv_len <= left.bv_len )
+ {
+ if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
+ return 1;
+ }
+
+ } else if ( val->bv_len >= left.bv_len ) {
+ if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
+ return(1);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static slap_access_t
+aci_list_get_attr_rights(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ /* loop through each rights/attr pair, skip first part (action) */
+ ACL_INIT(mask);
+ for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
+ if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
+ continue;
+ }
+
+ if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
+ continue;
+ }
+
+ mask |= aci_list_map_rights( &bv );
+ }
+
+ return mask;
+}
+
+static int
+aci_list_get_rights(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ struct berval perm, actn;
+ slap_access_t *mask;
+ int i, found;
+
+ if ( attr == NULL || BER_BVISEMPTY( attr )
+ || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
+ {
+ attr = &aci_bv[ ACI_BV_BR_ENTRY ];
+ }
+
+ found = 0;
+ ACL_INIT(*grant);
+ ACL_INIT(*deny);
+ /* loop through each permissions clause */
+ for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
+ if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
+ mask = grant;
+
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
+ mask = deny;
+
+ } else {
+ continue;
+ }
+
+ found = 1;
+ *mask |= aci_list_get_attr_rights( &perm, attr, val );
+ *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
+ }
+
+ return found;
+}
+
+static int
+aci_group_member (
+ struct berval *subj,
+ const struct berval *defgrpoc,
+ const struct berval *defgrpat,
+ Operation *op,
+ Entry *e,
+ int nmatch,
+ regmatch_t *matches
+)
+{
+ struct berval subjdn;
+ struct berval grpoc;
+ struct berval grpat;
+ ObjectClass *grp_oc = NULL;
+ AttributeDescription *grp_ad = NULL;
+ const char *text;
+ int rc;
+
+ /* format of string is "group/objectClassValue/groupAttrName" */
+ if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
+ return 0;
+ }
+
+ if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
+ grpoc = *defgrpoc;
+ }
+
+ if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
+ grpat = *defgrpat;
+ }
+
+ rc = slap_bv2ad( &grpat, &grp_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 0;
+ goto done;
+ }
+ rc = 0;
+
+ grp_oc = oc_bvfind( &grpoc );
+
+ if ( grp_oc != NULL && grp_ad != NULL ) {
+ char buf[ ACI_BUF_SIZE ];
+ struct berval bv, ndn;
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = (char *)&buf;
+ if ( acl_string_expand( &bv, &subjdn,
+ e->e_ndn, nmatch, matches ) )
+ {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
+ {
+ rc = ( backend_group( op, e, &ndn, &op->o_ndn,
+ grp_oc, grp_ad ) == 0 );
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+done:
+ return rc;
+}
+
+int
+aci_mask(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny,
+ slap_aci_scope_t asserted_scope )
+{
+ struct berval bv, scope, perms, type, sdn;
+ int rc;
+
+
+ assert( !BER_BVISNULL( &desc->ad_cname ) );
+
+ /* parse an aci of the form:
+ oid # scope # action;rights;attr;rights;attr
+ $ action;rights;attr;rights;attr # type # subject
+
+ [NOTE: the following comment is very outdated,
+ as the draft version it refers to (Ando, 2004-11-20)].
+
+ See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
+ a full description of the format for this attribute.
+ Differences: "this" in the draft is "self" here, and
+ "self" and "public" is in the position of type.
+
+ <scope> = {entry|children|subtree}
+ <type> = {public|users|access-id|subtree|onelevel|children|
+ self|dnattr|group|role|set|set-ref}
+
+ This routine now supports scope={ENTRY,CHILDREN}
+ with the semantics:
+ - ENTRY applies to "entry" and "subtree";
+ - CHILDREN aplies to "children" and "subtree"
+ */
+
+ /* check that the aci has all 5 components */
+ if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
+ return 0;
+ }
+
+ /* check that the aci family is supported */
+ /* FIXME: the OID is ignored? */
+ if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
+ return 0;
+ }
+
+ /* check that the scope matches */
+ if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
+ return 0;
+ }
+
+ /* note: scope can be either ENTRY or CHILDREN;
+ * they respectively match "entry" and "children" in bv
+ * both match "subtree" */
+ switch ( asserted_scope ) {
+ case SLAP_ACI_SCOPE_ENTRY:
+ /* TODO: use ber_bvcmp */
+ if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
+ && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
+ {
+ return 0;
+ }
+ break;
+
+ case SLAP_ACI_SCOPE_CHILDREN:
+ /* TODO: use ber_bvcmp */
+ if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
+ && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
+ {
+ return 0;
+ }
+ break;
+
+ default:
+ /* TODO: add assertion */
+ return 0;
+ }
+
+ /* get the list of permissions clauses, bail if empty */
+ if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
+ /* TODO: add assertion */
+ return 0;
+ }
+
+ /* check if any permissions allow desired access */
+ if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
+ return 0;
+ }
+
+ /* see if we have a DN match */
+ if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
+ /* TODO: add assertion */
+ return 0;
+ }
+
+ /* see if we have a public (i.e. anonymous) access */
+ /* TODO: use ber_bvcmp */
+ if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
+ return 1;
+ }
+
+ /* otherwise require an identity */
+ if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
+ return 0;
+ }
+
+ /* see if we have a users access */
+ /* TODO: use ber_bvcmp */
+ if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
+ return 1;
+ }
+
+ /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
+ * just grab all the berval up to its end (ITS#3303).
+ * NOTE: the problem could be solved by providing the DN with
+ * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
+ * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
+#if 0
+ if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
+ return 0;
+ }
+#endif
+ sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
+ sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
+
+ /* TODO: use ber_bvcmp */
+ if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
+ struct berval ndn;
+
+ /* TODO: don't normalize */
+ rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ( dn_match( &op->o_ndn, &ndn ) ) {
+ rc = 1;
+ }
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+ return rc;
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
+ struct berval ndn;
+
+ /* TODO: don't normalize */
+ rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
+ rc = 1;
+ }
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+ return rc;
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
+ struct berval ndn, pndn;
+
+ /* TODO: don't normalize */
+ rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ dnParent( &ndn, &pndn );
+
+ if ( dn_match( &op->o_ndn, &pndn ) ) {
+ rc = 1;
+ }
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+ return rc;
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
+ struct berval ndn;
+
+ /* TODO: don't normalize */
+ rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ( !dn_match( &op->o_ndn, &ndn )
+ && dnIsSuffix( &op->o_ndn, &ndn ) )
+ {
+ rc = 1;
+ }
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+ return rc;
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
+ if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
+ return 1;
+ }
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
+ Attribute *at;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_bv2ad( &sdn, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ /* TODO: add assertion */
+ return 0;
+ }
+
+ rc = 0;
+ for ( at = attrs_find( e->e_attrs, ad );
+ at != NULL;
+ at = attrs_find( at->a_next, ad ) )
+ {
+ if ( value_find_ex( ad,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ at->a_nvals,
+ &op->o_ndn, op->o_tmpmemctx ) == 0 )
+ {
+ rc = 1;
+ break;
+ }
+ }
+
+ return rc;
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
+ if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
+ &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
+ {
+ return 1;
+ }
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
+ if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
+ &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
+ {
+ return 1;
+ }
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
+ if ( acl_match_set( &sdn, op, e, 0 ) ) {
+ return 1;
+ }
+
+ /* TODO: use ber_bvcmp */
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
+ if ( acl_match_set( &sdn, op, e, 1 ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef SLAP_DYNACL
+/*
+ * FIXME: there is a silly dependence that makes it difficult
+ * to move ACIs in a run-time loadable module under the "dynacl"
+ * umbrella, because sets share some helpers with ACIs.
+ */
+static int
+dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
+{
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ fprintf( stderr, "%s: line %d: "
+ "inappropriate style \"%s\" in \"aci\" by clause\n",
+ fname, lineno, style_strings[sty] );
+ return -1;
+ }
+
+ if ( right != NULL && *right != '\0' ) {
+ if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
+ fprintf( stderr,
+ "%s: line %d: aci \"%s\": %s\n",
+ fname, lineno, right, text );
+ return -1;
+ }
+
+ } else {
+ ad = slap_schema.si_ad_aci;
+ }
+
+ if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
+ fprintf( stderr, "%s: line %d: "
+ "aci \"%s\": inappropriate syntax: %s\n",
+ fname, lineno, right,
+ ad->ad_type->sat_syntax_oid );
+ return -1;
+ }
+
+ *privp = (void *)ad;
+
+ return 0;
+}
+
+static int
+dynacl_aci_unparse( void *priv, struct berval *bv )
+{
+ AttributeDescription *ad = ( AttributeDescription * )priv;
+ char *ptr;
+
+ assert( ad != NULL );
+
+ bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
+ ptr = lutil_strcopy( bv->bv_val, " aci=" );
+ ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+dynacl_aci_mask(
+ void *priv,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grantp,
+ slap_access_t *denyp )
+{
+ AttributeDescription *ad = ( AttributeDescription * )priv;
+ Attribute *at;
+ slap_access_t tgrant, tdeny, grant, deny;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+ char accessmaskbuf1[ACCESSMASK_MAXLEN];
+#endif /* LDAP_DEBUG */
+
+ /* start out with nothing granted, nothing denied */
+ ACL_INIT(tgrant);
+ ACL_INIT(tdeny);
+
+ /* get the aci attribute */
+ at = attr_find( e->e_attrs, ad );
+ if ( at != NULL ) {
+ int i;
+
+ /* the aci is an multi-valued attribute. The
+ * rights are determined by OR'ing the individual
+ * rights given by the acis.
+ */
+ for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
+ if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
+ nmatch, matches, &grant, &deny,
+ SLAP_ACI_SCOPE_ENTRY ) != 0 )
+ {
+ tgrant |= grant;
+ tdeny |= deny;
+ }
+ }
+
+ Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+ accessmask2str( tgrant, accessmaskbuf, 1 ),
+ accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
+ }
+
+ /* If the entry level aci didn't contain anything valid for the
+ * current operation, climb up the tree and evaluate the
+ * acis with scope set to subtree
+ */
+ if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
+ struct berval parent_ndn;
+
+#if 1
+ /* to solve the chicken'n'egg problem of accessing
+ * the OpenLDAPaci attribute, the direct access
+ * to the entry's attribute is unchecked; however,
+ * further accesses to OpenLDAPaci values in the
+ * ancestors occur through backend_attribute(), i.e.
+ * with the identity of the operation, requiring
+ * further access checking. For uniformity, this
+ * makes further requests occur as the rootdn, if
+ * any, i.e. searching for the OpenLDAPaci attribute
+ * is considered an internal search. If this is not
+ * acceptable, then the same check needs be performed
+ * when accessing the entry's attribute. */
+ Operation op2 = *op;
+
+ if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ }
+#endif
+
+ dnParent( &e->e_nname, &parent_ndn );
+ while ( !BER_BVISEMPTY( &parent_ndn ) ){
+ int i;
+ BerVarray bvals = NULL;
+ int ret, stop;
+
+ Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
+ ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+
+ switch ( ret ) {
+ case LDAP_SUCCESS :
+ stop = 0;
+ if ( !bvals ) {
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
+ if ( aci_mask( op, e, desc, val,
+ &bvals[i],
+ nmatch, matches,
+ &grant, &deny,
+ SLAP_ACI_SCOPE_CHILDREN ) != 0 )
+ {
+ tgrant |= grant;
+ tdeny |= deny;
+ /* evaluation stops as soon as either a "deny" or a
+ * "grant" directive matches.
+ */
+ if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
+ stop = 1;
+ }
+ }
+ Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+ accessmask2str( tgrant, accessmaskbuf, 1 ),
+ accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
+ }
+ break;
+
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ /* just go on if the aci-Attribute is not present in
+ * the current entry
+ */
+ Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
+ stop = 0;
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* We have reached the base object */
+ Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
+ stop = 1;
+ break;
+
+ default:
+ stop = 1;
+ break;
+ }
+
+ if ( stop ) {
+ break;
+ }
+ dnParent( &parent_ndn, &parent_ndn );
+ }
+ }
+
+ *grantp = tgrant;
+ *denyp = tdeny;
+
+ return 0;
+}
+
+/* need to register this at some point */
+static slap_dynacl_t dynacl_aci = {
+ "aci",
+ dynacl_aci_parse,
+ dynacl_aci_unparse,
+ dynacl_aci_mask,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+dynacl_aci_init( void )
+{
+ return slap_dynacl_register( &dynacl_aci );
+}
+
+#endif /* SLAP_DYNACL */
+
+/* ACI syntax validation */
+
+/*
+ * Matches given berval to array of bervals
+ * Returns:
+ * >=0 if one if the array elements equals to this berval
+ * -1 if string was not found in array
+ */
+static int
+bv_getcaseidx(
+ struct berval *bv,
+ const struct berval *arr[] )
+{
+ int i;
+
+ if ( BER_BVISEMPTY( bv ) ) {
+ return -1;
+ }
+
+ for ( i = 0; arr[ i ] != NULL ; i++ ) {
+ if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/* Returns what have left in input berval after current sub */
+static void
+bv_get_tail(
+ struct berval *val,
+ struct berval *sub,
+ struct berval *tail )
+{
+ int head_len;
+
+ tail->bv_val = sub->bv_val + sub->bv_len;
+ head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
+ tail->bv_len = val->bv_len - head_len;
+}
+
+
+/*
+ * aci is accepted in following form:
+ * oid#scope#rights#type#subject
+ * Where:
+ * oid := numeric OID
+ * scope := entry|children
+ * rights := right[[$right]...]
+ * right := (grant|deny);action
+ * action := perms;attr[[;perms;attr]...]
+ * perms := perm[[,perm]...]
+ * perm := c|s|r|w|x
+ * attr := attributeType|[all]
+ * type := public|users|self|dnattr|group|role|set|set-ref|
+ * access_id|subtree|onelevel|children
+ */
+static int
+OpenLDAPaciValidatePerms(
+ struct berval *perms )
+{
+ int i;
+
+ for ( i = 0; i < perms->bv_len; ) {
+ switch ( perms->bv_val[ i ] ) {
+ case 'c':
+ case 's':
+ case 'r':
+ case 'w':
+ case 'x':
+ break;
+
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ++i == perms->bv_len ) {
+ return LDAP_SUCCESS;
+ }
+
+ while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
+ i++;
+
+ assert( i != perms->bv_len );
+
+ if ( perms->bv_val[ i ] != ',' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ do {
+ i++;
+ } while ( perms->bv_val[ i ] == ' ' );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static const struct berval *ACIgrantdeny[] = {
+ &aci_bv[ ACI_BV_GRANT ],
+ &aci_bv[ ACI_BV_DENY ],
+ NULL
+};
+
+static int
+OpenLDAPaciValidateRight(
+ struct berval *action )
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ /* grant|deny */
+ if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
+ bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
+ if ( i & 1 ) {
+ /* perms */
+ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ /* attr */
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ /* could be "[all]" or an attribute description */
+ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+ continue;
+ }
+
+ if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+
+ /* "perms;attr" go in pairs */
+ if ( i > 0 && ( i & 1 ) == 0 ) {
+ return LDAP_SUCCESS;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+OpenLDAPaciNormalizeRight(
+ struct berval *action,
+ struct berval *naction,
+ void *ctx )
+{
+ struct berval grantdeny,
+ perms = BER_BVNULL,
+ bv = BER_BVNULL;
+ int idx,
+ i;
+
+ /* grant|deny */
+ if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
+ if ( idx == -1 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
+
+ for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
+ if ( i & 1 ) {
+ /* perms */
+ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+ perms = bv;
+
+ } else {
+ /* attr */
+ char *ptr;
+
+ /* could be "[all]" or an attribute description */
+ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+ bv = aci_bv[ ACI_BV_BR_ALL ];
+
+ } else {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_bv2ad( &bv, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv = ad->ad_cname;
+ }
+
+ naction->bv_val = ber_memrealloc_x( naction->bv_val,
+ naction->bv_len + STRLENOF( ";" )
+ + perms.bv_len + STRLENOF( ";" )
+ + bv.bv_len + 1,
+ ctx );
+
+ ptr = &naction->bv_val[ naction->bv_len ];
+ ptr[ 0 ] = ';';
+ ptr++;
+ ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
+ ptr[ 0 ] = ';';
+ ptr++;
+ ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
+ ptr[ 0 ] = '\0';
+ naction->bv_len += STRLENOF( ";" ) + perms.bv_len
+ + STRLENOF( ";" ) + bv.bv_len;
+ }
+ }
+
+ /* perms;attr go in pairs */
+ if ( i > 1 && ( i & 1 ) ) {
+ return LDAP_SUCCESS;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+}
+
+static int
+OpenLDAPaciValidateRights(
+ struct berval *actions )
+
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
+ if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+OpenLDAPaciNormalizeRights(
+ struct berval *actions,
+ struct berval *nactions,
+ void *ctx )
+
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ BER_BVZERO( nactions );
+ for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
+ int rc;
+ struct berval nbv;
+
+ rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree_x( nactions->bv_val, ctx );
+ BER_BVZERO( nactions );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( i == 0 ) {
+ *nactions = nbv;
+
+ } else {
+ nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
+ nactions->bv_len + STRLENOF( "$" )
+ + nbv.bv_len + 1,
+ ctx );
+ nactions->bv_val[ nactions->bv_len ] = '$';
+ AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
+ nbv.bv_val, nbv.bv_len + 1 );
+ ber_memfree_x( nbv.bv_val, ctx );
+ nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
+ }
+ BER_BVZERO( &nbv );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static const struct berval *OpenLDAPaciscopes[] = {
+ &aci_bv[ ACI_BV_ENTRY ],
+ &aci_bv[ ACI_BV_CHILDREN ],
+
+ NULL
+};
+
+static const struct berval *OpenLDAPacitypes[] = {
+ /* DN-valued */
+ &aci_bv[ ACI_BV_GROUP ],
+ &aci_bv[ ACI_BV_ROLE ],
+
+/* set to one past the last DN-valued type with options (/) */
+#define LAST_OPTIONAL 2
+
+ &aci_bv[ ACI_BV_ACCESS_ID ],
+ &aci_bv[ ACI_BV_SUBTREE ],
+ &aci_bv[ ACI_BV_ONELEVEL ],
+ &aci_bv[ ACI_BV_CHILDREN ],
+
+/* set to one past the last DN-valued type */
+#define LAST_DNVALUED 6
+
+ /* non DN-valued */
+ &aci_bv[ ACI_BV_DNATTR ],
+ &aci_bv[ ACI_BV_PUBLIC ],
+ &aci_bv[ ACI_BV_USERS ],
+ &aci_bv[ ACI_BV_SELF ],
+ &aci_bv[ ACI_BV_SET ],
+ &aci_bv[ ACI_BV_SET_REF ],
+
+ NULL
+};
+
+int
+OpenLDAPaciValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ struct berval oid = BER_BVNULL,
+ scope = BER_BVNULL,
+ rights = BER_BVNULL,
+ type = BER_BVNULL,
+ subject = BER_BVNULL;
+ int idx;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* oid */
+ if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
+ numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
+ {
+ /* NOTE: the numericoidValidate() is rather pedantic;
+ * I'd replace it with X-ORDERED VALUES so that
+ * it's guaranteed values are maintained and used
+ * in the desired order */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* scope */
+ if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
+ bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* rights */
+ if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
+ OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* type */
+ if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &type, OpenLDAPacitypes );
+ if ( idx == -1 ) {
+ struct berval isgr;
+
+ if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
+ if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ /* subject */
+ bv_get_tail( val, &type, &subject );
+ if ( subject.bv_val[ 0 ] != '#' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( idx >= LAST_DNVALUED ) {
+ if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_bv2ad( &subject, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+ /* FIXME: allow nameAndOptionalUID? */
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ /* not a DN */
+ return LDAP_SUCCESS;
+
+ } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
+ || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
+ {
+ /* do {group|role}/oc/at check */
+ struct berval ocbv = BER_BVNULL,
+ atbv = BER_BVNULL;
+
+ ocbv.bv_val = strchr( type.bv_val, '/' );
+ if ( ocbv.bv_val != NULL ) {
+ ocbv.bv_val++;
+
+ atbv.bv_val = strchr( ocbv.bv_val, '/' );
+ if ( atbv.bv_val != NULL ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ atbv.bv_val++;
+ atbv.bv_len = type.bv_len
+ - ( atbv.bv_val - type.bv_val );
+ ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
+
+ rc = slap_bv2ad( &atbv, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ ocbv.bv_len = type.bv_len
+ - ( ocbv.bv_val - type.bv_val );
+ }
+
+ if ( oc_bvfind( &ocbv ) == NULL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+
+ if ( BER_BVISEMPTY( &subject ) ) {
+ /* empty DN invalid */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ subject.bv_val++;
+ subject.bv_len--;
+
+ /* FIXME: pass DN syntax? */
+ return dnValidate( NULL, &subject );
+}
+
+static int
+OpenLDAPaciPrettyNormal(
+ struct berval *val,
+ struct berval *out,
+ void *ctx,
+ int normalize )
+{
+ struct berval oid = BER_BVNULL,
+ scope = BER_BVNULL,
+ rights = BER_BVNULL,
+ nrights = BER_BVNULL,
+ type = BER_BVNULL,
+ ntype = BER_BVNULL,
+ subject = BER_BVNULL,
+ nsubject = BER_BVNULL;
+ int idx,
+ rc,
+ freesubject = 0,
+ freetype = 0;
+ char *ptr;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* oid: if valid, it's already normalized */
+ if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
+ numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* scope: normalize by replacing with OpenLDAPaciscopes */
+ if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
+ if ( idx == -1 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ scope = *OpenLDAPaciscopes[ idx ];
+
+ /* rights */
+ if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
+ != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* type */
+ if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+ idx = bv_getcaseidx( &type, OpenLDAPacitypes );
+ if ( idx == -1 ) {
+ struct berval isgr;
+
+ if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
+ if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+ }
+ ntype = *OpenLDAPacitypes[ idx ];
+
+ /* subject */
+ bv_get_tail( val, &type, &subject );
+
+ if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ subject.bv_val++;
+ subject.bv_len--;
+
+ if ( idx < LAST_DNVALUED ) {
+ /* FIXME: pass DN syntax? */
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL,
+ &subject, &nsubject, ctx );
+ } else {
+ rc = dnPretty( NULL, &subject, &nsubject, ctx );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ freesubject = 1;
+
+ } else {
+ goto cleanup;
+ }
+
+ if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
+ || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
+ {
+ /* do {group|role}/oc/at check */
+ struct berval ocbv = BER_BVNULL,
+ atbv = BER_BVNULL;
+
+ ocbv.bv_val = strchr( type.bv_val, '/' );
+ if ( ocbv.bv_val != NULL ) {
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+ struct berval bv;
+
+ bv.bv_len = ntype.bv_len;
+
+ ocbv.bv_val++;
+
+ atbv.bv_val = strchr( ocbv.bv_val, '/' );
+ if ( atbv.bv_val != NULL ) {
+ atbv.bv_val++;
+ atbv.bv_len = type.bv_len
+ - ( atbv.bv_val - type.bv_val );
+ ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
+
+ rc = slap_bv2ad( &atbv, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
+
+ } else {
+ ocbv.bv_len = type.bv_len
+ - ( ocbv.bv_val - type.bv_val );
+ }
+
+ if ( oc_bvfind( &ocbv ) == NULL ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
+ bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
+
+ ptr = bv.bv_val;
+ ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr,
+ oc->soc_cname.bv_val,
+ oc->soc_cname.bv_len );
+ if ( ad != NULL ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr,
+ ad->ad_cname.bv_val,
+ ad->ad_cname.bv_len );
+ }
+ ptr[ 0 ] = '\0';
+
+ ntype = bv;
+ freetype = 1;
+ }
+ }
+
+ } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_bv2ad( &subject, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+ /* FIXME: allow nameAndOptionalUID? */
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ nsubject = ad->ad_cname;
+ }
+
+
+ out->bv_len =
+ oid.bv_len + STRLENOF( "#" )
+ + scope.bv_len + STRLENOF( "#" )
+ + rights.bv_len + STRLENOF( "#" )
+ + ntype.bv_len + STRLENOF( "#" )
+ + nsubject.bv_len;
+
+ out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
+ ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ if ( !BER_BVISNULL( &nsubject ) ) {
+ ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
+ }
+ ptr[ 0 ] = '\0';
+
+cleanup:;
+ if ( freesubject ) {
+ ber_memfree_x( nsubject.bv_val, ctx );
+ }
+
+ if ( freetype ) {
+ ber_memfree_x( ntype.bv_val, ctx );
+ }
+
+ if ( !BER_BVISNULL( &nrights ) ) {
+ ber_memfree_x( nrights.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+int
+OpenLDAPaciPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
+}
+
+int
+OpenLDAPaciNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
+}
+
+#endif /* SLAPD_ACI_ENABLED */
+
/*
* speed up compares
*/
-static struct berval
- aci_bv_entry = BER_BVC("entry"),
- aci_bv_children = BER_BVC("children"),
- aci_bv_onelevel = BER_BVC("onelevel"),
- aci_bv_subtree = BER_BVC("subtree"),
- aci_bv_br_entry = BER_BVC("[entry]"),
- aci_bv_br_all = BER_BVC("[all]"),
- aci_bv_access_id = BER_BVC("access-id"),
+const struct berval aci_bv[] = {
+ BER_BVC("entry"),
+ BER_BVC("children"),
+ BER_BVC("onelevel"),
+ BER_BVC("subtree"),
+ BER_BVC("[entry]"),
+ BER_BVC("[all]"),
+ BER_BVC("access-id"),
#if 0
- aci_bv_anonymous = BER_BVC("anonymous"),
+ BER_BVC("anonymous"),
#endif
- aci_bv_public = BER_BVC("public"),
- aci_bv_users = BER_BVC("users"),
- aci_bv_self = BER_BVC("self"),
- aci_bv_dnattr = BER_BVC("dnattr"),
- aci_bv_group = BER_BVC("group"),
- aci_bv_role = BER_BVC("role"),
- aci_bv_set = BER_BVC("set"),
- aci_bv_set_ref = BER_BVC("set-ref"),
- aci_bv_grant = BER_BVC("grant"),
- aci_bv_deny = BER_BVC("deny"),
-
- aci_bv_ip_eq = BER_BVC("IP="),
+ BER_BVC("public"),
+ BER_BVC("users"),
+ BER_BVC("self"),
+ BER_BVC("dnattr"),
+ BER_BVC("group"),
+ BER_BVC("role"),
+ BER_BVC("set"),
+ BER_BVC("set-ref"),
+ BER_BVC("grant"),
+ BER_BVC("deny"),
+
+ BER_BVC("IP="),
#ifdef LDAP_PF_LOCAL
- aci_bv_path_eq = BER_BVC("PATH="),
+ BER_BVC("PATH="),
#if 0
- aci_bv_dirsep = BER_BVC(LDAP_DIRSEP),
+ BER_BVC(LDAP_DIRSEP),
#endif
#endif /* LDAP_PF_LOCAL */
- aci_bv_group_class = BER_BVC(SLAPD_GROUP_CLASS),
- aci_bv_group_attr = BER_BVC(SLAPD_GROUP_ATTR),
- aci_bv_role_class = BER_BVC(SLAPD_ROLE_CLASS),
- aci_bv_role_attr = BER_BVC(SLAPD_ROLE_ATTR),
- aci_bv_set_attr = BER_BVC(SLAPD_ACI_SET_ATTR);
-
-typedef enum slap_aci_scope_t {
- SLAP_ACI_SCOPE_ENTRY = 0x1,
- SLAP_ACI_SCOPE_CHILDREN = 0x2,
- SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
-} slap_aci_scope_t;
+ BER_BVC(SLAPD_GROUP_CLASS),
+ BER_BVC(SLAPD_GROUP_ATTR),
+ BER_BVC(SLAPD_ROLE_CLASS),
+ BER_BVC(SLAPD_ROLE_ATTR),
+
+ BER_BVC(SLAPD_ACI_SET_ATTR)
+};
static AccessControl * slap_acl_get(
AccessControl *ac, int *count,
int count,
AccessControlState *state );
-#ifdef SLAPD_ACI_ENABLED
-static int aci_mask(
- Operation *op, Entry *e,
- AttributeDescription *desc,
- struct berval *val,
- struct berval *aci,
- int nmatch,
- regmatch_t *matches,
- slap_access_t *grant,
- slap_access_t *deny,
- slap_aci_scope_t scope);
-#endif /* SLAPD_ACI_ENABLED */
-
static int regex_matches(
struct berval *pat, char *str, char *buf,
int nmatch, regmatch_t *matches);
-static int string_expand(
- struct berval *newbuf, struct berval *pattern,
- char *match, int nmatch, regmatch_t *matches);
-typedef struct AciSetCookie {
+typedef struct AclSetCookie {
Operation *op;
Entry *e;
-} AciSetCookie;
+} AclSetCookie;
-SLAP_SET_GATHER aci_set_gather;
-SLAP_SET_GATHER aci_set_gather2;
-static int aci_match_set ( struct berval *subj, Operation *op,
- Entry *e, int setref );
+SLAP_SET_GATHER acl_set_gather;
+SLAP_SET_GATHER acl_set_gather2;
/*
* access_allowed - check whether op->o_ndn is allowed the requested access
return 1;
}
- if ( string_expand( &bv, &b->a_pat,
+ if ( acl_string_expand( &bv, &b->a_pat,
e->e_nname.bv_val,
tmp_nmatch, tmp_matchesp ) )
{
AccessControlState *state )
{
int i;
- Access *b;
+ Access *b;
#ifdef LDAP_DEBUG
- char accessmaskbuf[ACCESSMASK_MAXLEN];
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
#if !defined( SLAP_DYNACL ) && defined( SLAPD_ACI_ENABLED )
- char accessmaskbuf1[ACCESSMASK_MAXLEN];
+ char accessmaskbuf1[ACCESSMASK_MAXLEN];
#endif /* !SLAP_DYNACL && SLAPD_ACI_ENABLED */
#endif /* DEBUG */
- const char *attr;
+ const char *attr;
+ slap_mask_t a2pmask = ACL_ACCESS2PRIV( *mask );
assert( a != NULL );
assert( mask != NULL );
bv.bv_len = sizeof( buf ) - 1;
bv.bv_val = buf;
- if ( string_expand( &bv, &b->a_sockurl_pat,
+ if ( acl_string_expand( &bv, &b->a_sockurl_pat,
e->e_ndn, nmatch, matches ) )
{
continue;
bv.bv_len = sizeof(buf) - 1;
bv.bv_val = buf;
- if ( string_expand(&bv, &b->a_domain_pat,
+ if ( acl_string_expand(&bv, &b->a_domain_pat,
e->e_ndn, nmatch, matches) )
{
continue;
bv.bv_len = sizeof( buf ) - 1;
bv.bv_val = buf;
- if ( string_expand( &bv, &b->a_peername_pat,
+ if ( acl_string_expand( &bv, &b->a_peername_pat,
e->e_ndn, nmatch, matches ) )
{
continue;
int port_number = -1;
if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
- aci_bv_ip_eq.bv_val, aci_bv_ip_eq.bv_len ) != 0 )
+ aci_bv[ ACI_BV_IP_EQ ].bv_val,
+ aci_bv[ ACI_BV_IP_EQ ].bv_len ) != 0 )
continue;
- ip.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_ip_eq.bv_len;
- ip.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_ip_eq.bv_len;
+ ip.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv[ ACI_BV_IP_EQ ].bv_len;
+ ip.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv[ ACI_BV_IP_EQ ].bv_len;
port = strrchr( ip.bv_val, ':' );
if ( port ) {
struct berval path;
if ( strncmp( op->o_conn->c_peer_name.bv_val,
- aci_bv_path_eq.bv_val, aci_bv_path_eq.bv_len ) != 0 )
+ aci_bv[ ACI_BV_PATH_EQ ].bv_val,
+ aci_bv[ ACI_BV_PATH_EQ ].bv_len ) != 0 )
continue;
- path.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_path_eq.bv_len;
- path.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_path_eq.bv_len;
+ path.bv_val = op->o_conn->c_peer_name.bv_val
+ + aci_bv[ ACI_BV_PATH_EQ ].bv_len;
+ path.bv_len = op->o_conn->c_peer_name.bv_len
+ - aci_bv[ ACI_BV_PATH_EQ ].bv_len;
if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
continue;
bv.bv_len = sizeof( buf ) - 1;
bv.bv_val = buf;
- if ( string_expand( &bv, &b->a_sockname_pat,
+ if ( acl_string_expand( &bv, &b->a_sockname_pat,
e->e_ndn, nmatch, matches ) )
{
continue;
continue;
}
- if ( string_expand( &bv, &b->a_group_pat,
+ if ( acl_string_expand( &bv, &b->a_group_pat,
e->e_nname.bv_val,
tmp_nmatch, tmp_matchesp ) )
{
continue;
}
- if ( string_expand( &bv, &b->a_set_pat,
+ if ( acl_string_expand( &bv, &b->a_set_pat,
e->e_nname.bv_val,
tmp_nmatch, tmp_matchesp ) )
{
bv = b->a_set_pat;
}
- if ( aci_match_set( &bv, op, e, 0 ) == 0 ) {
+ if ( acl_match_set( &bv, op, e, 0 ) == 0 ) {
continue;
}
}
/* first check if the right being requested
* is allowed by the ACL clause.
*/
- if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
+ if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
continue;
}
ACL_INIT(tdeny);
for ( da = b->a_dynacl; da; da = da->da_next ) {
- slap_access_t grant, deny;
+ slap_access_t grant,
+ deny;
+
+ ACL_INIT(grant);
+ ACL_INIT(deny);
Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n",
da->da_name, 0, 0 );
* rights given by the acis.
*/
for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
- if (aci_mask( op,
+ if ( aci_mask( op,
e, desc, val,
&at->a_nvals[i],
nmatch, matches,
- &grant, &deny, SLAP_ACI_SCOPE_ENTRY ) != 0)
+ &grant, &deny, SLAP_ACI_SCOPE_ENTRY ) != 0 )
{
tgrant |= grant;
tdeny |= deny;
break;
}
- for( i = 0; bvals[i].bv_val != NULL; i++){
+ for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
#if 0
/* FIXME: this breaks acl caching;
* see also ACL_RECORD_VALUE_STATE above */
ACL_RECORD_VALUE_STATE;
#endif
- if (aci_mask(op, e, desc, val, &bvals[i],
+ if ( aci_mask( op, e, desc, val, &bvals[i],
nmatch, matches,
&grant, &deny, SLAP_ACI_SCOPE_CHILDREN ) != 0 )
{
*mask = modmask;
}
+ a2pmask = *mask;
+
Debug( LDAP_DEBUG_ACL,
"<= acl_mask: [%d] mask: %s\n",
i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
return( ret );
}
-static int
-aci_get_part(
+int
+acl_get_part(
struct berval *list,
int ix,
char sep,
return bv->bv_len;
}
-typedef struct aci_set_gather_t {
+typedef struct acl_set_gather_t {
SetCookie *cookie;
BerVarray bvals;
-} aci_set_gather_t;
+} acl_set_gather_t;
static int
-aci_set_cb_gather( Operation *op, SlapReply *rs )
+acl_set_cb_gather( Operation *op, SlapReply *rs )
{
- aci_set_gather_t *p = (aci_set_gather_t *)op->o_callback->sc_private;
+ acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private;
if ( rs->sr_type == REP_SEARCH ) {
BerValue bvals[ 2 ];
}
BerVarray
-aci_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
+acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
{
- AciSetCookie *cp = (AciSetCookie *)cookie;
+ AclSetCookie *cp = (AclSetCookie *)cookie;
int rc = 0;
LDAPURLDesc *ludp = NULL;
Operation op2 = { 0 };
SlapReply rs = {REP_RESULT};
AttributeName anlist[ 2 ], *anlistp = NULL;
int nattrs = 0;
- slap_callback cb = { NULL, aci_set_cb_gather, NULL, NULL };
- aci_set_gather_t p = { 0 };
+ slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL };
+ acl_set_gather_t p = { 0 };
const char *text = NULL;
static struct berval defaultFilter_bv = BER_BVC( "(objectClass=*)" );
* also return the syntax or some "comparison cookie".
*/
if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
- return aci_set_gather2( cookie, name, desc );
+ return acl_set_gather2( cookie, name, desc );
}
rc = ldap_url_parse( name->bv_val, &ludp );
}
BerVarray
-aci_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
+acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
{
- AciSetCookie *cp = (AciSetCookie *)cookie;
+ AclSetCookie *cp = (AclSetCookie *)cookie;
BerVarray bvals = NULL;
struct berval ndn;
int rc = 0;
return bvals;
}
-static int
-aci_match_set (
+int
+acl_match_set (
struct berval *subj,
Operation *op,
Entry *e,
- int setref
-)
+ int setref )
{
struct berval set = BER_BVNULL;
int rc = 0;
- AciSetCookie cookie;
+ AclSetCookie cookie;
if ( setref == 0 ) {
ber_dupbv_x( &set, subj, op->o_tmpmemctx );
AttributeDescription *desc = NULL;
/* format of string is "entry/setAttrName" */
- if ( aci_get_part( subj, 0, '/', &subjdn ) < 0 ) {
+ if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
return 0;
}
- if ( aci_get_part( subj, 1, '/', &setat ) < 0 ) {
- setat = aci_bv_set_attr;
+ if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
+ setat = aci_bv[ ACI_BV_SET_ATTR ];
}
/*
if ( !BER_BVISNULL( &set ) ) {
cookie.op = op;
cookie.e = e;
- rc = ( slap_set_filter( aci_set_gather, (SetCookie *)&cookie, &set,
+ rc = ( slap_set_filter( acl_set_gather, (SetCookie *)&cookie, &set,
&op->o_ndn, &e->e_nname, NULL ) > 0 );
slap_sl_free( set.bv_val, op->o_tmpmemctx );
}
return(rc);
}
-#ifdef SLAPD_ACI_ENABLED
-static int
-aci_list_map_rights(
- struct berval *list )
-{
- struct berval bv;
- slap_access_t mask;
- int i;
-
- ACL_INIT(mask);
- for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
- if (bv.bv_len <= 0)
- continue;
- switch (*bv.bv_val) {
- case 'c':
- ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
- break;
- case 's':
- /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
- * the right 's' to mean "set", but in the examples states
- * that the right 's' means "search". The latter definition
- * is used here.
- */
- ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
- break;
- case 'r':
- ACL_PRIV_SET(mask, ACL_PRIV_READ);
- break;
- case 'w':
- ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
- break;
- case 'x':
- /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
- * define any equivalent to the AUTH right, so I've just used
- * 'x' for now.
- */
- ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
- break;
- default:
- break;
- }
-
- }
- return(mask);
-}
-
-static int
-aci_list_has_attr(
- struct berval *list,
- const struct berval *attr,
- struct berval *val )
-{
- struct berval bv, left, right;
- int i;
-
- for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
- if (aci_get_part(&bv, 0, '=', &left) < 0
- || aci_get_part(&bv, 1, '=', &right) < 0)
- {
- if (ber_bvstrcasecmp(attr, &bv) == 0)
- return(1);
- } else if (val == NULL) {
- if (ber_bvstrcasecmp(attr, &left) == 0)
- return(1);
- } else {
- if (ber_bvstrcasecmp(attr, &left) == 0) {
- /* this is experimental code that implements a
- * simple (prefix) match of the attribute value.
- * the ACI draft does not provide for aci's that
- * apply to specific values, but it would be
- * nice to have. If the <attr> part of an aci's
- * rights list is of the form <attr>=<value>,
- * that means the aci applies only to attrs with
- * the given value. Furthermore, if the attr is
- * of the form <attr>=<value>*, then <value> is
- * treated as a prefix, and the aci applies to
- * any value with that prefix.
- *
- * Ideally, this would allow r.e. matches.
- */
- if (aci_get_part(&right, 0, '*', &left) < 0
- || right.bv_len <= left.bv_len)
- {
- if (ber_bvstrcasecmp(val, &right) == 0)
- return(1);
- } else if (val->bv_len >= left.bv_len) {
- if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
- return(1);
- }
- }
- }
- }
- return(0);
-}
-
-static slap_access_t
-aci_list_get_attr_rights(
- struct berval *list,
- const struct berval *attr,
- struct berval *val )
-{
- struct berval bv;
- slap_access_t mask;
- int i;
-
- /* loop through each rights/attr pair, skip first part (action) */
- ACL_INIT(mask);
- for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
- if (aci_list_has_attr(&bv, attr, val) == 0)
- continue;
- if (aci_get_part(list, i, ';', &bv) < 0)
- continue;
- mask |= aci_list_map_rights(&bv);
- }
- return(mask);
-}
-
-static int
-aci_list_get_rights(
- struct berval *list,
- const struct berval *attr,
- struct berval *val,
- slap_access_t *grant,
- slap_access_t *deny )
-{
- struct berval perm, actn;
- slap_access_t *mask;
- int i, found;
-
- if (attr == NULL || attr->bv_len == 0
- || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
- attr = &aci_bv_br_entry;
- }
-
- found = 0;
- ACL_INIT(*grant);
- ACL_INIT(*deny);
- /* loop through each permissions clause */
- for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
- if (aci_get_part(&perm, 0, ';', &actn) < 0)
- continue;
- if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
- mask = grant;
- } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
- mask = deny;
- } else {
- continue;
- }
-
- found = 1;
- *mask |= aci_list_get_attr_rights(&perm, attr, val);
- *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
- }
- return(found);
-}
-
-static int
-aci_group_member (
- struct berval *subj,
- struct berval *defgrpoc,
- struct berval *defgrpat,
- Operation *op,
- Entry *e,
- int nmatch,
- regmatch_t *matches
-)
-{
- struct berval subjdn;
- struct berval grpoc;
- struct berval grpat;
- ObjectClass *grp_oc = NULL;
- AttributeDescription *grp_ad = NULL;
- const char *text;
- int rc;
-
- /* format of string is "group/objectClassValue/groupAttrName" */
- if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
- return(0);
- }
-
- if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
- grpoc = *defgrpoc;
- }
-
- if (aci_get_part(subj, 2, '/', &grpat) < 0) {
- grpat = *defgrpat;
- }
-
- rc = slap_bv2ad( &grpat, &grp_ad, &text );
- if( rc != LDAP_SUCCESS ) {
- rc = 0;
- goto done;
- }
- rc = 0;
-
- grp_oc = oc_bvfind( &grpoc );
-
- if (grp_oc != NULL && grp_ad != NULL ) {
- char buf[ACL_BUF_SIZE];
- struct berval bv, ndn;
- bv.bv_len = sizeof( buf ) - 1;
- bv.bv_val = (char *)&buf;
- if ( string_expand(&bv, &subjdn,
- e->e_ndn, nmatch, matches) )
- {
- rc = LDAP_OTHER;
- goto done;
- }
- if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
- rc = ( backend_group( op, e, &ndn, &op->o_ndn,
- grp_oc, grp_ad ) == 0 );
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
- }
- }
-
-done:
- return(rc);
-}
-
-static int
-aci_mask(
- Operation *op,
- Entry *e,
- AttributeDescription *desc,
- struct berval *val,
- struct berval *aci,
- int nmatch,
- regmatch_t *matches,
- slap_access_t *grant,
- slap_access_t *deny,
- slap_aci_scope_t asserted_scope
-)
-{
- struct berval bv, scope, perms, type, sdn;
- int rc;
-
-
- assert( !BER_BVISNULL( &desc->ad_cname ) );
-
- /* parse an aci of the form:
- oid # scope # action;rights;attr;rights;attr
- $ action;rights;attr;rights;attr # type # subject
-
- [NOTE: the following comment is very outdated,
- as the draft version it refers to (Ando, 2004-11-20)].
-
- See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
- a full description of the format for this attribute.
- Differences: "this" in the draft is "self" here, and
- "self" and "public" is in the position of type.
-
- <scope> = {entry|children|subtree}
- <type> = {public|users|access-id|subtree|onelevel|children|
- self|dnattr|group|role|set|set-ref}
-
- This routine now supports scope={ENTRY,CHILDREN}
- with the semantics:
- - ENTRY applies to "entry" and "subtree";
- - CHILDREN aplies to "children" and "subtree"
- */
-
- /* check that the aci has all 5 components */
- if ( aci_get_part( aci, 4, '#', NULL ) < 0 ) {
- return 0;
- }
-
- /* check that the aci family is supported */
- if ( aci_get_part( aci, 0, '#', &bv ) < 0 ) {
- return 0;
- }
-
- /* check that the scope matches */
- if ( aci_get_part( aci, 1, '#', &scope ) < 0 ) {
- return 0;
- }
-
- /* note: scope can be either ENTRY or CHILDREN;
- * they respectively match "entry" and "children" in bv
- * both match "subtree" */
- switch ( asserted_scope ) {
- case SLAP_ACI_SCOPE_ENTRY:
- if ( ber_bvstrcasecmp( &scope, &aci_bv_entry ) != 0
- && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
- {
- return 0;
- }
- break;
-
- case SLAP_ACI_SCOPE_CHILDREN:
- if ( ber_bvstrcasecmp( &scope, &aci_bv_children ) != 0
- && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
- {
- return 0;
- }
- break;
-
- default:
- return 0;
- }
-
- /* get the list of permissions clauses, bail if empty */
- if ( aci_get_part( aci, 2, '#', &perms ) <= 0 ) {
- return 0;
- }
-
- /* check if any permissions allow desired access */
- if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
- return 0;
- }
-
- /* see if we have a DN match */
- if ( aci_get_part( aci, 3, '#', &type ) < 0 ) {
- return 0;
- }
-
- /* see if we have a public (i.e. anonymous) access */
- if ( ber_bvstrcasecmp( &aci_bv_public, &type ) == 0 ) {
- return 1;
- }
-
- /* otherwise require an identity */
- if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
- return 0;
- }
-
- /* see if we have a users access */
- if ( ber_bvstrcasecmp( &aci_bv_users, &type ) == 0 ) {
- return 1;
- }
-
- /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
- * just grab all the berval up to its end (ITS#3303).
- * NOTE: the problem could be solved by providing the DN with
- * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
- * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
-#if 0
- if ( aci_get_part( aci, 4, '#', &sdn ) < 0 ) {
- return 0;
- }
-#endif
- sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
- sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
-
- if ( ber_bvstrcasecmp( &aci_bv_access_id, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( dn_match( &op->o_ndn, &ndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_subtree, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_onelevel, &type ) == 0 ) {
- struct berval ndn, pndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- dnParent( &ndn, &pndn );
-
- if ( dn_match( &op->o_ndn, &pndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_children, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( !dn_match( &op->o_ndn, &ndn )
- && dnIsSuffix( &op->o_ndn, &ndn ) )
- {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_self, &type ) == 0 ) {
- if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_dnattr, &type ) == 0 ) {
- Attribute *at;
- AttributeDescription *ad = NULL;
- const char *text;
-
- rc = slap_bv2ad( &sdn, &ad, &text );
-
- if( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- rc = 0;
-
- for ( at = attrs_find( e->e_attrs, ad );
- at != NULL;
- at = attrs_find( at->a_next, ad ) )
- {
- if ( value_find_ex( ad,
- SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
- SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
- at->a_nvals,
- &op->o_ndn, op->o_tmpmemctx ) == 0 )
- {
- rc = 1;
- break;
- }
- }
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_group, &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv_group_class,
- &aci_bv_group_attr, op, e, nmatch, matches ) )
- {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_role, &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv_role_class,
- &aci_bv_role_attr, op, e, nmatch, matches ) )
- {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_set, &type ) == 0 ) {
- if ( aci_match_set( &sdn, op, e, 0 ) ) {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_set_ref, &type ) == 0 ) {
- if ( aci_match_set( &sdn, op, e, 1 ) ) {
- return 1;
- }
- }
-
- return 0;
-}
-
-#ifdef SLAP_DYNACL
-/*
- * FIXME: there is a silly dependence that makes it difficult
- * to move ACIs in a run-time loadable module under the "dynacl"
- * umbrella, because sets share some helpers with ACIs.
- */
-static int
-dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
-{
- AttributeDescription *ad = NULL;
- const char *text = NULL;
-
- if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
- fprintf( stderr, "%s: line %d: "
- "inappropriate style \"%s\" in \"aci\" by clause\n",
- fname, lineno, style_strings[sty] );
- return -1;
- }
-
- if ( right != NULL && *right != '\0' ) {
- if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
- fprintf( stderr,
- "%s: line %d: aci \"%s\": %s\n",
- fname, lineno, right, text );
- return -1;
- }
-
- } else {
- ad = slap_schema.si_ad_aci;
- }
-
- if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
- fprintf( stderr, "%s: line %d: "
- "aci \"%s\": inappropriate syntax: %s\n",
- fname, lineno, right,
- ad->ad_type->sat_syntax_oid );
- return -1;
- }
-
- *privp = (void *)ad;
-
- return 0;
-}
-
-static int
-dynacl_aci_unparse( void *priv, struct berval *bv )
-{
- AttributeDescription *ad = ( AttributeDescription * )priv;
- char *ptr;
-
- assert( ad != NULL );
-
- bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
- ptr = lutil_strcopy( bv->bv_val, " aci=" );
- ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
- bv->bv_len = ptr - bv->bv_val;
-
- return 0;
-}
-
-
-static int
-dynacl_aci_mask(
- void *priv,
- Operation *op,
- Entry *e,
- AttributeDescription *desc,
- struct berval *val,
- int nmatch,
- regmatch_t *matches,
- slap_access_t *grantp,
- slap_access_t *denyp )
-{
- AttributeDescription *ad = ( AttributeDescription * )priv;
- Attribute *at;
- slap_access_t tgrant, tdeny, grant, deny;
-#ifdef LDAP_DEBUG
- char accessmaskbuf[ACCESSMASK_MAXLEN];
- char accessmaskbuf1[ACCESSMASK_MAXLEN];
-#endif /* LDAP_DEBUG */
-
- /* start out with nothing granted, nothing denied */
- ACL_INIT(tgrant);
- ACL_INIT(tdeny);
-
- /* get the aci attribute */
- at = attr_find( e->e_attrs, ad );
- if ( at != NULL ) {
- int i;
-
- /* the aci is an multi-valued attribute. The
- * rights are determined by OR'ing the individual
- * rights given by the acis.
- */
- for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
- if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
- nmatch, matches, &grant, &deny,
- SLAP_ACI_SCOPE_ENTRY ) != 0 )
- {
- tgrant |= grant;
- tdeny |= deny;
- }
- }
-
- Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
- accessmask2str( tgrant, accessmaskbuf, 1 ),
- accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
- }
-
- /* If the entry level aci didn't contain anything valid for the
- * current operation, climb up the tree and evaluate the
- * acis with scope set to subtree
- */
- if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
- struct berval parent_ndn;
-
-#if 1
- /* to solve the chicken'n'egg problem of accessing
- * the OpenLDAPaci attribute, the direct access
- * to the entry's attribute is unchecked; however,
- * further accesses to OpenLDAPaci values in the
- * ancestors occur through backend_attribute(), i.e.
- * with the identity of the operation, requiring
- * further access checking. For uniformity, this
- * makes further requests occur as the rootdn, if
- * any, i.e. searching for the OpenLDAPaci attribute
- * is considered an internal search. If this is not
- * acceptable, then the same check needs be performed
- * when accessing the entry's attribute. */
- Operation op2 = *op;
-
- if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
- op2.o_dn = op->o_bd->be_rootdn;
- op2.o_ndn = op->o_bd->be_rootndn;
- }
-#endif
-
- dnParent( &e->e_nname, &parent_ndn );
- while ( !BER_BVISEMPTY( &parent_ndn ) ){
- int i;
- BerVarray bvals = NULL;
- int ret, stop;
-
- Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
- ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
-
- switch ( ret ) {
- case LDAP_SUCCESS :
- stop = 0;
- if ( !bvals ) {
- break;
- }
-
- for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++) {
- if ( aci_mask( op, e, desc, val,
- &bvals[i],
- nmatch, matches,
- &grant, &deny,
- SLAP_ACI_SCOPE_CHILDREN ) != 0 )
- {
- tgrant |= grant;
- tdeny |= deny;
- /* evaluation stops as soon as either a "deny" or a
- * "grant" directive matches.
- */
- if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
- stop = 1;
- }
- }
- Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
- accessmask2str( tgrant, accessmaskbuf, 1 ),
- accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
- }
- break;
-
- case LDAP_NO_SUCH_ATTRIBUTE:
- /* just go on if the aci-Attribute is not present in
- * the current entry
- */
- Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
- stop = 0;
- break;
-
- case LDAP_NO_SUCH_OBJECT:
- /* We have reached the base object */
- Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
- stop = 1;
- break;
-
- default:
- stop = 1;
- break;
- }
-
- if ( stop ) {
- break;
- }
- dnParent( &parent_ndn, &parent_ndn );
- }
- }
-
- *grantp = tgrant;
- *denyp = tdeny;
-
- return 0;
-}
-
-/* need to register this at some point */
-static slap_dynacl_t dynacl_aci = {
- "aci",
- dynacl_aci_parse,
- dynacl_aci_unparse,
- dynacl_aci_mask,
- NULL,
- NULL,
- NULL
-};
-
-#endif /* SLAP_DYNACL */
-
-#endif /* SLAPD_ACI_ENABLED */
-
#ifdef SLAP_DYNACL
/*
acl_init( void )
{
#ifdef SLAP_DYNACL
- int i, rc;
- slap_dynacl_t *known_dynacl[] = {
-#ifdef SLAPD_ACI_ENABLED
- &dynacl_aci,
-#endif /* SLAPD_ACI_ENABLED */
- NULL
- };
+ int rc;
- for ( i = 0; known_dynacl[ i ]; i++ ) {
- rc = slap_dynacl_register( known_dynacl[ i ] );
- if ( rc ) {
- return rc;
- }
+#ifdef SLAPD_ACI_ENABLED
+ rc = dynacl_aci_init();
+ if ( rc != 0 ) {
+ return rc;
}
+#endif /* SLAPD_ACI_ENABLED */
+
#endif /* SLAP_DYNACL */
return 0;
}
-static int
-string_expand(
+int
+acl_string_expand(
struct berval *bv,
struct berval *pat,
char *match,
*dp = '\0';
bv->bv_len = size;
- Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
- Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
+ Debug( LDAP_DEBUG_TRACE, "=> acl_string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
return 0;
}
str = "";
};
- string_expand( &bv, pat, buf, nmatch, matches );
+ acl_string_expand( &bv, pat, buf, nmatch, matches );
rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
if ( rc ) {
char error[ACL_BUF_SIZE];
}
} else {
- b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
+ b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
if( b->a_group_oc == NULL ) {
fprintf( stderr,
if ( !BER_BVISNULL( &a->a_group_pat ) ) {
free( a->a_group_pat.bv_val );
}
+ if ( a->a_dynacl != NULL ) {
+ slap_dynacl_t *da;
+ for ( da = a->a_dynacl; da; ) {
+ slap_dynacl_t *tmp = da;
+
+ da = da->da_next;
+
+ if ( tmp->da_destroy ) {
+ tmp->da_destroy( tmp->da_private );
+ }
+
+ ch_free( tmp );
+ }
+ }
free( a );
}
filter_free( a->acl_filter );
}
if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
+ regfree( &a->acl_dn_re );
+ }
free ( a->acl_dn_pat.bv_val );
}
if ( a->acl_attrs ) {
for ( da = b->a_dynacl; da; da = da->da_next ) {
if ( da->da_unparse ) {
- struct berval bv;
+ struct berval bv = BER_BVNULL;
(void)( *da->da_unparse )( da->da_private, &bv );
+ assert( !BER_BVISNULL( &bv ) );
ptr = lutil_strcopy( ptr, bv.bv_val );
ch_free( bv.bv_val );
}
if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
+#ifdef SLAP_ZONE_ALLOC
+ slap_zn_runlock(bdb->bi_cache.c_zctx, e);
+#endif
+ bdb_cache_return_entry_r( bdb->bi_dbenv,
+ &bdb->bi_cache, e, &lock );
+ e = NULL;
send_paged_response( op, rs, &lastid, tentries );
goto done;
}
return 0;
}
+#ifdef HAVE_TLS
static int
-ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+ldap_back_start_tls(
+ LDAP *ld,
+ int protocol,
+ int *is_tls,
+ const char *url,
+ unsigned flags,
+ const char **text )
{
- struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
- int vers = op->o_protocol;
- LDAP *ld = NULL;
-
- assert( lcp != NULL );
-
- rs->sr_err = ldap_initialize( &ld, li->url );
- if ( rs->sr_err != LDAP_SUCCESS ) {
- goto error_return;
- }
-
- /* Set LDAP version. This will always succeed: If the client
- * bound with a particular version, then so can we.
- */
- ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
+ int rc = LDAP_SUCCESS;
+ struct ldapinfo li;
- /* automatically chase referrals ("[dont-]chase-referrals" statement) */
- if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
- ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
- }
+ /* this is ridicolous... */
+ li.flags = flags;
-#ifdef HAVE_TLS
/* start TLS ("tls-[try-]{start,propagate}" statements) */
- if ( ( LDAP_BACK_USE_TLS( li ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( li ) ) )
- && !ldap_is_ldaps_url( li->url ) )
+ if ( ( LDAP_BACK_USE_TLS( &li ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &li ) ) )
+ && !ldap_is_ldaps_url( url ) )
{
#ifdef SLAP_STARTTLS_ASYNCHRONOUS
/*
*/
int msgid;
- rs->sr_err = ldap_start_tls( ld, NULL, NULL, &msgid );
- if ( rs->sr_err == LDAP_SUCCESS ) {
+ if ( protocol == 0 ) {
+ ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&protocol );
+ }
+
+ if ( protocol < LDAP_VERSION3 ) {
+ protocol = LDAP_VERSION3;
+ /* Set LDAP version */
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (const void *)&protocol );
+ }
+
+ rc = ldap_start_tls( ld, NULL, NULL, &msgid );
+ if ( rc == LDAP_SUCCESS ) {
LDAPMessage *res = NULL;
- int rc, retries = 1;
+ int retries = 1;
struct timeval tv = { 0, 0 };
retry:;
rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
if ( rc < 0 ) {
- rs->sr_err = LDAP_OTHER;
+ rc = LDAP_OTHER;
} else if ( rc == 0 ) {
if ( retries ) {
tv.tv_usec = 100000;
goto retry;
}
- rs->sr_err = LDAP_OTHER;
+ rc = LDAP_OTHER;
} else if ( rc == LDAP_RES_EXTENDED ) {
struct berval *data = NULL;
- rs->sr_err = ldap_parse_extended_result( ld, res,
+ rc = ldap_parse_extended_result( ld, res,
NULL, &data, 0 );
- if ( rs->sr_err == LDAP_SUCCESS ) {
- rs->sr_err = ldap_result2error( ld, res, 1 );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_result2error( ld, res, 1 );
res = NULL;
/* FIXME: in case a referral
* is returned, should we try
* using it instead of the
* configured URI? */
- if ( rs->sr_err == LDAP_SUCCESS ) {
- ldap_install_tls( ld );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_install_tls( ld );
- } else if ( rs->sr_err == LDAP_REFERRAL ) {
- rs->sr_err = LDAP_OTHER;
- rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
+ } else if ( rc == LDAP_REFERRAL ) {
+ rc = LDAP_OTHER;
+ *text = "unwilling to chase referral returned by Start TLS exop";
}
if ( data ) {
}
} else {
- rs->sr_err = LDAP_OTHER;
+ rc = LDAP_OTHER;
}
if ( res != NULL ) {
/*
* use synchronous StartTLS
*/
- rs->sr_err = ldap_start_tls_s( ld, NULL, NULL );
+ rc = ldap_start_tls_s( ld, NULL, NULL );
#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
/* if StartTLS is requested, only attempt it if the URL
* is not "ldaps://"; this may occur not only in case
* of misconfiguration, but also when used in the chain
* overlay, where the "uri" can be parsed out of a referral */
- if ( rs->sr_err == LDAP_SERVER_DOWN
- || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
- {
- ldap_unbind_ext( ld, NULL, NULL );
- goto error_return;
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ *is_tls = 1;
+ break;
+
+ case LDAP_SERVER_DOWN:
+ break;
+
+ default:
+ if ( LDAP_BACK_TLS_CRITICAL( &li ) ) {
+ *text = "could not start TLS";
+ break;
+ }
+
+ /* in case Start TLS is not critical */
+ *is_tls = 0;
+ rc = LDAP_SUCCESS;
+ break;
}
- /* in case Start TLS is not critical */
- rs->sr_err = LDAP_SUCCESS;
+ } else {
+ *is_tls = 0;
+ }
+
+ return rc;
+}
+#endif /* HAVE_TLS */
+
+static int
+ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+ int vers = op->o_protocol;
+ LDAP *ld = NULL;
+#ifdef HAVE_TLS
+ int is_tls = op->o_conn->c_is_tls;
+#endif /* HAVE_TLS */
+
+ assert( lcp != NULL );
+
+ rs->sr_err = ldap_initialize( &ld, li->url );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto error_return;
+ }
+
+ /* Set LDAP version. This will always succeed: If the client
+ * bound with a particular version, then so can we.
+ */
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
+
+ /* automatically chase referrals ("[dont-]chase-referrals" statement) */
+ if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
+ ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
+ }
+
+#ifdef HAVE_TLS
+ rs->sr_err = ldap_back_start_tls( ld,
+ op->o_protocol, &is_tls,
+ li->url, li->flags, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ goto error_return;
}
#endif /* HAVE_TLS */
if ( *lcp == NULL ) {
*lcp = (struct ldapconn *)ch_calloc( 1, sizeof( struct ldapconn ) );
+ (*lcp)->lc_flags= li->flags;
}
(*lcp)->lc_ld = ld;
(*lcp)->lc_refcnt = 1;
+#ifdef HAVE_TLS
+ (*lcp)->lc_is_tls = is_tls;
+#endif /* HAVE_TLS */
error_return:;
if ( rs->sr_err != LDAP_SUCCESS ) {
lc_curr.lc_conn = op->o_conn;
} else {
- lc_curr.lc_conn = NULL;
+#ifdef HAVE_TLS
+ if ( op->o_conn->c_is_tls ) {
+ lc_curr.lc_conn = LDAP_BACK_PRIV_CONN_TLS;
+ } else
+#endif /* HAVE_TLS */
+ {
+ lc_curr.lc_conn = LDAP_BACK_PRIV_CONN;
+ }
}
/* Internal searches are privileged and shared. So is root. */
/* FIXME: there seem to be concurrency issues */
if ( op->o_do_not_cache || be_isroot( op ) ) {
lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
- lc_curr.lc_conn = NULL;
+#ifdef HAVE_TLS
+ if ( op->o_conn->c_is_tls ) {
+ lc_curr.lc_conn = LDAP_BACK_PRIV_CONN_TLS;
+ } else
+#endif /* HAVE_TLS */
+ {
+ lc_curr.lc_conn = LDAP_BACK_PRIV_CONN;
+ }
lc_curr.lc_ispriv = 1;
} else {
}
}
+#ifdef HAVE_TLS
+ /* if start TLS failed but it was not mandatory,
+ * check if the non-TLS connection was already
+ * in cache; in case, destroy the newly created
+ * connection and use the existing one */
+ if ( lc->lc_conn == LDAP_BACK_PRIV_CONN_TLS
+ && !ldap_tls_inplace( lc->lc_ld ) )
+ {
+ struct ldapconn *tmplc;
+
+ lc_curr.lc_conn = LDAP_BACK_PRIV_CONN;
+ ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+ tmplc = (struct ldapconn *)avl_find( li->conntree,
+ (caddr_t)&lc_curr, ldap_back_conn_cmp );
+ if ( tmplc != NULL ) {
+ refcnt = ++tmplc->lc_refcnt;
+ ldap_back_conn_free( lc );
+ lc = tmplc;
+ }
+ ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+
+ if ( tmplc != NULL ) {
+ goto done;
+ }
+ }
+#endif /* HAVE_TLS */
+
lc->lc_bound = 0;
/* Inserts the newly created ldapconn in the avl tree */
"=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n",
(void *)lc, refcnt, 0 );
}
-
+
+done:;
return lc;
}
{
struct ldapconn *lc = (struct ldapconn *)params;
+#ifdef HAVE_TLS
+ /* ... otherwise we couldn't get here */
+ assert( lc != NULL );
+
+ if ( !ldap_tls_inplace( ld ) ) {
+ int is_tls = lc->lc_is_tls,
+ rc;
+ const char *text = NULL;
+
+ rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+#endif /* HAVE_TLS */
+
/* FIXME: add checks on the URL/identity? */
return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
struct berval bv;
+#ifdef SLAP_AUTHZ_SYNTAX
+ struct berval in;
+ int rc;
+ ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+ rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: %d: "
+ "\"idassert-authzFrom <authz>\": "
+ "invalid syntax.\n",
+ c->fname, c->lineno );
+ return 1;
+ }
+#else /* !SLAP_AUTHZ_SYNTAX */
ber_str2bv( c->argv[ 1 ], 0, 1, &bv );
+#endif /* !SLAP_AUTHZ_SYNTAX */
ber_bvarray_add( &li->idassert_authz, &bv );
} break;
return rs->sr_err = LDAP_PROTOCOL_ERROR;
}
+ Statslog( LDAP_DEBUG_STATS, "%s WHOAMI\n",
+ op->o_log_prefix, 0, 0, 0, 0 );
+
rs->sr_err = backend_check_restrictions( op, rs,
(struct berval *)&slap_EXOP_WHOAMI );
if( rs->sr_err != LDAP_SUCCESS ) return rs->sr_err;
BER_BVZERO( &rs->sr_ref[ cnt ] );
}
+ if ( match.bv_val != NULL ) {
+ if ( match.bv_val[ 0 ] == '\0' ) {
+ LDAP_FREE( match.bv_val );
+ BER_BVZERO( &match );
+ } else {
+ match.bv_len = strlen( match.bv_val );
+ }
+ }
+
/* cleanup */
if ( references ) {
ldap_value_free( references );
typedef struct metatarget_t {
char *mt_uri;
+ int mt_scope;
struct berval mt_psuffix; /* pretty suffix */
struct berval mt_nsuffix; /* normalized suffix */
extern int
meta_back_is_candidate(
struct berval *nsuffix,
+ int suffixscope,
struct berval *ndn,
int scope );
{
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
- int bound = 0, i;
+ int bound = 0,
+ i,
+ isroot = 0;
SlapReply *candidates = meta_back_candidates_get( op );
- ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
+ if ( be_isroot( op ) ) {
+ isroot = 1;
+ }
Debug( LDAP_DEBUG_TRACE,
- "%s meta_back_dobind: conn=%ld\n",
- op->o_log_prefix, mc->mc_conn->c_connid, 0 );
+ "%s meta_back_dobind: conn=%ld%s\n",
+ op->o_log_prefix, mc->mc_conn->c_connid,
+ isroot ? " (isroot)" : "" );
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
/*
* all the targets are bound as pseudoroot
continue;
}
- rc = meta_back_single_dobind( op, rs, mc, i,
+ if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
+ {
+ Operation op2 = *op;
+
+ op2.o_tag = LDAP_REQ_BIND;
+ op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
+ op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
+ op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
+ op2.orb_method = LDAP_AUTH_SIMPLE;
+
+ rc = meta_back_single_bind( &op2, rs, mc, i );
+
+ } else {
+ rc = meta_back_single_dobind( op, rs, mc, i,
LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
+ }
+
if ( rc != LDAP_SUCCESS ) {
rs->sr_err = slap_map_api2result( rs );
#include "portable.h"
#include <stdio.h>
+#include "ac/string.h"
#include "slap.h"
#include "../back-ldap/back-ldap.h"
int
meta_back_is_candidate(
struct berval *nsuffix,
+ int suffixscope,
struct berval *ndn,
int scope )
{
if ( dnIsSuffix( ndn, nsuffix ) ) {
- return META_CANDIDATE;
+ switch ( suffixscope ) {
+ case LDAP_SCOPE_SUBTREE:
+ default:
+ return META_CANDIDATE;
+
+#ifdef LDAP_SCOPE_SUBORDINATE
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( ndn->bv_len > nsuffix->bv_len ) {
+ return META_CANDIDATE;
+ }
+ break;
+#endif /* LDAP_SCOPE_SUBORDINATE */
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_ONELEVEL:
+ if ( ndn->bv_len > nsuffix->bv_len ) {
+ struct berval rdn = *ndn;
+
+ rdn.bv_len -= nsuffix->bv_len
+ + STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn ) ) {
+ return META_CANDIDATE;
+ }
+ }
+ break;
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_BASE:
+ if ( ndn->bv_len == nsuffix->bv_len ) {
+ return META_CANDIDATE;
+ }
+ break;
+ }
+
+ return META_NOT_CANDIDATE;
}
if ( scope == LDAP_SCOPE_SUBTREE && dnIsSuffix( nsuffix, ndn ) ) {
return META_NOT_CANDIDATE;
}
-#if 0
-/*
- * meta_back_is_candidate_unique
- *
- * checks whether a candidate is unique
- * Note: dn MUST be normalized
- */
-static int
-meta_back_is_candidate_unique(
- metainfo_t *mi,
- struct berval *ndn )
-{
- switch ( meta_back_select_unique_candidate( mi, ndn ) ) {
- case META_TARGET_MULTIPLE:
- case META_TARGET_NONE:
- return 0;
- }
-
- return 1;
-}
-#endif /* 0 */
-
/*
* meta_back_select_unique_candidate
*
int i, candidate = META_TARGET_NONE;
for ( i = 0; i < mi->mi_ntargets; ++i ) {
- if ( meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix, ndn, LDAP_SCOPE_BASE ) )
+ if ( meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix,
+ mi->mi_targets[ i ].mt_scope,
+ ndn, LDAP_SCOPE_BASE ) )
{
if ( candidate == META_TARGET_NONE ) {
candidate = i;
/*
* copies and stores uri and suffix
*/
- dn.bv_val = ludp->lud_dn;
- dn.bv_len = strlen( ludp->lud_dn );
-
+ ber_str2bv( ludp->lud_dn, 0, 0, &dn );
rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix,
&mi->mi_targets[ i ].mt_nsuffix, NULL );
if( rc != LDAP_SUCCESS ) {
ludp->lud_dn[ 0 ] = '\0';
+ switch ( ludp->lud_scope ) {
+ case LDAP_SCOPE_DEFAULT:
+ mi->mi_targets[ i ].mt_scope = LDAP_SCOPE_SUBTREE;
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+#ifdef LDAP_SCOPE_SUBORDINATE
+ case LDAP_SCOPE_SUBORDINATE:
+#endif /* LDAP_SCOPE_SUBORDINATE */
+ mi->mi_targets[ i ].mt_scope = ludp->lud_scope;
+ break;
+
+ default:
+ fprintf( stderr, "%s: line %d: "
+ "invalid scope for target '%s'\n",
+ fname, lineno, argv[ 1 ] );
+ return( 1 );
+ }
+
/* check all, to apply the scope check on the first one */
for ( tmpludp = ludp; tmpludp; tmpludp = tmpludp->lud_next ) {
if ( tmpludp->lud_dn != NULL && tmpludp->lud_dn[ 0 ] != '\0' ) {
return( 1 );
}
-
- if ( tmpludp->lud_scope == LDAP_SCOPE_BASE ) {
- tmpludp->lud_scope = LDAP_SCOPE_DEFAULT;
- }
}
mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp );
* a candidate, try using it (FIXME: YMMV) */
if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
&& meta_back_is_candidate( &mi->mi_targets[ mi->mi_defaulttarget ].mt_nsuffix,
+ mi->mi_targets[ mi->mi_defaulttarget ].mt_scope,
ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
{
candidate = mi->mi_defaulttarget;
}
break;
}
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
}
return candidate;
}
if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( new_conn ) {
+ meta_back_freeconn( op, mc );
+
+ } else {
+ meta_back_release_conn( op, mc );
+ }
+
if ( sendok & LDAP_BACK_SENDERR ) {
if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
rs->sr_text = NULL;
rs->sr_matched = NULL;
}
+
return NULL;
}
}
if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
{
+ if ( new_conn ) {
+ meta_back_freeconn( op, mc );
+
+ } else {
+ meta_back_release_conn( op, mc );
+ }
+
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
rs->sr_text = "cross-target rename not supported";
if ( sendok & LDAP_BACK_SENDERR ) {
send_ldap_result( op, rs );
rs->sr_text = NULL;
}
+
return NULL;
}
for ( i = 0; i < mi->mi_ntargets; i++ ) {
if ( i == cached
|| meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix,
+ mi->mi_targets[ i ].mt_scope,
&op->o_req_ndn, LDAP_SCOPE_SUBTREE ) )
{
&op->o_req_ndn ) )
{
realbase = mi->mi_targets[ candidate ].mt_nsuffix;
+#ifdef LDAP_SCOPE_SUBORDINATE
+ if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ }
+#endif /* LDAP_SCOPE_SUBORDINATE */
} else {
/*
realbase = mi->mi_targets[ candidate ].mt_nsuffix;
#ifdef LDAP_SCOPE_SUBORDINATE
if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
- realscope = LDAP_SCOPE_SUBTREE;
+ if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ } else {
+ realscope = LDAP_SCOPE_SUBTREE;
+ }
} else
#endif /* LDAP_SCOPE_SUBORDINATE */
{
*
* FIXME: only the last one gets caught!
*/
- if ( candidate_match > 0 && rs->sr_nentries > 0 ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)mi->mi_ntargets;
+ if ( candidate_match > 0 ) {
+ struct berval pmatched = BER_BVNULL;
+
/* we use the first one */
for ( i = 0; i < mi->mi_ntargets; i++ ) {
if ( candidates[ i ].sr_tag == META_CANDIDATE
&& candidates[ i ].sr_matched )
{
- matched = (char *)candidates[ i ].sr_matched;
- candidates[ i ].sr_matched = NULL;
- break;
+ struct berval bv, pbv;
+ int rc;
+
+ ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
+ rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
+
+ if ( rc == LDAP_SUCCESS ) {
+
+ /* NOTE: if they all are superiors
+ * of the baseDN, the shorter is also
+ * superior of the longer... */
+ if ( pbv.bv_len > pmatched.bv_len ) {
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
+ }
+ pmatched = pbv;
+ op->o_private = (void *)i;
+
+ } else {
+ op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( candidates[ i ].sr_matched != NULL ) {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ }
}
}
+
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ matched = pmatched.bv_val;
+ }
+
+ } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
+ matched = ch_strdup( op->o_bd->be_suffix[ 0 ].bv_val );
}
#if 0
rs->sr_err = sres;
rs->sr_matched = matched;
rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
- savepriv = op->o_private;
- op->o_private = (void *)mi->mi_ntargets;
send_ldap_result( op, rs );
op->o_private = savepriv;
rs->sr_matched = NULL;
finish:;
if ( matched ) {
- free( matched );
+ op->o_tmpfree( matched, op->o_tmpmemctx );
}
if ( rs->sr_v2ref ) {
meta_back_release_conn( op, mc );
- return rc;
+ return rs->sr_err;
}
static int
return LDAP_PROTOCOL_ERROR;
}
+ Statslog( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n",
+ op->o_log_prefix, opid, 0, 0, 0 );
+
ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
if ( o->o_msgid == opid ) {
static ldap_pvt_thread_mutex_t conn_nextid_mutex;
static unsigned long conn_nextid = 0;
+static const char conn_lost_str[] = "connection lost";
+
/* structure state (protected by connections_mutex) */
#define SLAP_C_UNINITIALIZED 0x00 /* MUST BE ZERO (0) */
#define SLAP_C_UNUSED 0x01
ldap_pvt_thread_mutex_lock( &connections[i].c_mutex );
/* connections_mutex and c_mutex are locked */
- connection_closing( &connections[i] );
+ connection_closing( &connections[i], "slapd shutdown" );
connection_close( &connections[i] );
ldap_pvt_thread_mutex_unlock( &connections[i].c_mutex );
if( difftime( c->c_activitytime+global_idletimeout, now) < 0 ) {
/* close it */
- connection_closing( c );
+ connection_closing( c, "idletimeout" );
connection_close( c );
i++;
}
if ( flags == CONN_IS_CLIENT ) {
c->c_conn_state = SLAP_C_CLIENT;
c->c_struct_state = SLAP_C_USED;
+ c->c_close_reason = "?"; /* should never be needed */
ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
ldap_pvt_thread_mutex_unlock( &c->c_mutex );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
c->c_conn_state = SLAP_C_INACTIVE;
c->c_struct_state = SLAP_C_USED;
+ c->c_close_reason = "?"; /* should never be needed */
c->c_ssf = c->c_transport_ssf = ssf;
c->c_tls_ssf = 0;
/* note: connections_mutex should be locked by caller */
ber_socket_t sd;
unsigned long connid;
+ const char *close_reason;
assert( connections != NULL );
assert( c != NULL );
/* only for stats (print -1 as "%lu" may give unexpected results ;) */
connid = c->c_connid;
+ close_reason = c->c_close_reason;
backend_connection_destroy(c);
if ( sd != AC_SOCKET_INVALID ) {
slapd_remove( sd, 1, 0 );
- Statslog( LDAP_DEBUG_STATS,
- "conn=%lu fd=%ld closed\n",
- connid, (long) sd, 0, 0, 0 );
+ Statslog( LDAP_DEBUG_STATS, (close_reason
+ ? "conn=%lu fd=%ld closed (%s)\n"
+ : "conn=%lu fd=%ld closed\n"),
+ connid, (long) sd, close_reason, 0, 0 );
}
ber_sockbuf_free( c->c_sb );
c->c_conn_state = SLAP_C_INVALID;
c->c_struct_state = SLAP_C_UNUSED;
+ c->c_close_reason = "?"; /* should never be needed */
#ifdef LDAP_SLAPI
/* call destructors, then constructors; avoids unnecessary allocation */
}
}
-void connection_closing( Connection *c )
+void connection_closing( Connection *c, const char *why )
{
assert( connections != NULL );
assert( c != NULL );
c->c_connid, sd, 0 );
/* update state to closing */
c->c_conn_state = SLAP_C_CLOSING;
+ c->c_close_reason = why;
/* don't listen on this port anymore */
slapd_clr_read( sd, 1 );
ldap_pvt_thread_yield();
ldap_pvt_thread_mutex_lock( &c->c_mutex );
}
+ } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
+ /* Client closed connection after doing Unbind. */
+ c->c_close_reason = NULL;
}
}
case LBER_ERROR:
case LDAP_REQ_UNBIND:
/* c_mutex is locked */
- connection_closing( conn );
+ connection_closing(
+ conn, tag == LDAP_REQ_UNBIND ? NULL : "operations error" );
break;
case LDAP_REQ_BIND:
c->c_listener = NULL;
c->c_conn_state = SLAP_C_INVALID;
c->c_struct_state = SLAP_C_UNUSED;
+ c->c_close_reason = "?"; /* should never be needed */
connection_return( c );
slapd_remove( s, 0, 1 );
}
s, rc, c->c_connid );
c->c_needs_tls_accept = 0;
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, "TLS negotiation failure" );
#if 0
/* Drain input before close, to allow SSL error codes
"unable to get TLS client DN, error=%d id=%lu\n",
s, rc, c->c_connid );
}
+ Statslog( LDAP_DEBUG_STATS,
+ "conn=%lu TLS established tls_ssf=%u ssf=%u\n",
+ c->c_connid, c->c_tls_ssf, c->c_ssf, 0, 0 );
slap_sasl_external( c, c->c_tls_ssf, &authid );
if ( authid.bv_val ) free( authid.bv_val );
}
"error=%d id=%lu, closing\n",
s, rc, c->c_connid );
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, "SASL layer install failure" );
connection_close( c );
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
"connection_read(%d): input error=%d id=%lu, closing.\n",
s, rc, c->c_connid );
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, conn_lost_str );
connection_close( c );
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
asserted->bv_val,
asserted->bv_len );
} else {
- return 1;
+ match = 1;
}
}
asserted->bv_val,
asserted->bv_len );
} else {
- return 1;
+ match = 1;
}
}
match = dnIsOneLevelRDN( &rdn ) ? 0 : 1;
}
} else {
- return 1;
+ match = 1;
}
}
/* check for controls inappropriate for all extended operations */
if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) {
+ Statslog( LDAP_DEBUG_STATS, "%s EXT oid=%s\n",
+ op->o_log_prefix, op->ore_reqoid.bv_val, 0, 0, 0 );
send_ldap_error( op, rs,
LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
"manageDSAit control inappropriate" );
if( !(ext = find_extop(supp_ext_list, &op->ore_reqoid )))
{
+ Statslog( LDAP_DEBUG_STATS, "%s EXT oid=%s\n",
+ op->o_log_prefix, op->ore_reqoid.bv_val, 0, 0, 0 );
Debug( LDAP_DEBUG_ANY, "do_extended: unsupported operation \"%s\"\n",
op->ore_reqoid.bv_val, 0 ,0 );
send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
return LDAP_PROTOCOL_ERROR;
}
+ Statslog( LDAP_DEBUG_STATS, "%s WHOAMI\n",
+ op->o_log_prefix, 0, 0, 0, 0 );
+
op->o_bd = op->o_conn->c_authz_backend;
if( backend_check_restrictions( op, rs,
(struct berval *)&slap_EXOP_WHOAMI ) != LDAP_SUCCESS ) {
/* Reset the restricted flag for the next session on this connection */
static int
-ppolicy_unbind( Operation *op, SlapReply *rs )
+ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
{
- pwcons[op->o_conn->c_conn_idx].restricted = 0;
+ pwcons[conn->c_conn_idx].restricted = 0;
return SLAP_CB_CONTINUE;
}
ppolicy.on_bi.bi_op_add = ppolicy_add;
ppolicy.on_bi.bi_op_bind = ppolicy_bind;
- ppolicy.on_bi.bi_op_unbind = ppolicy_unbind;
ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
ppolicy.on_bi.bi_op_modify = ppolicy_modify;
ppolicy.on_bi.bi_op_search = ppolicy_restrict;
+ ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
return overlay_register( &ppolicy );
}
* was not checkpointed at the previous shutdown.
*
* 2: when the current contextCSN is known and we have a sync cookie, we search
- * for one entry with CSN <= the cookie CSN. (Used to search for =.) If an
- * entry is found, the cookie CSN is valid, otherwise it is stale.
+ * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
+ * If an entry is found, the cookie CSN is valid, otherwise it is stale.
*
* 3: during a refresh phase, we search for all entries with CSN <= the cookie
* CSN, and generate Present records for them. We always collect this result
int i, rc = LDAP_SUCCESS;
fpres_cookie pcookie;
sync_control *srs = NULL;
+ int findcsn_retry = 1;
if ( mode != FIND_MAXCSN ) {
srs = op->o_controls[slap_cids.sc_LDAPsync];
fop.ors_filter = &cf;
fop.ors_filterstr.bv_val = buf;
+again:
switch( mode ) {
case FIND_MAXCSN:
cf.f_choice = LDAP_FILTER_GE;
maxcsn.bv_len = si->si_ctxcsn.bv_len;
break;
case FIND_CSN:
- cf.f_choice = LDAP_FILTER_LE;
cf.f_av_value = srs->sr_state.ctxcsn;
- fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN<=%s)",
- cf.f_av_value.bv_val );
+ /* Look for exact match the first time */
+ if ( findcsn_retry ) {
+ cf.f_choice = LDAP_FILTER_EQUALITY;
+ fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN=%s)",
+ cf.f_av_value.bv_val );
+ /* On retry, look for <= */
+ } else {
+ cf.f_choice = LDAP_FILTER_LE;
+ fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN<=%s)",
+ cf.f_av_value.bv_val );
+ }
fop.ors_attrsonly = 1;
fop.ors_attrs = slap_anlist_no_attrs;
fop.ors_slimit = 1;
break;
case FIND_CSN:
/* If matching CSN was not found, invalidate the context. */
- if ( !cb.sc_private ) rc = LDAP_NO_SUCH_OBJECT;
+ if ( !cb.sc_private ) {
+ /* If we didn't find an exact match, then try for <= */
+ if ( findcsn_retry ) {
+ findcsn_retry = 0;
+ goto again;
+ }
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
break;
case FIND_PRESENT:
op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
slap_overinst *ss_on;
syncops *ss_so;
int ss_present;
+ struct berval ss_ctxcsn;
+ char ss_csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
} searchstate;
static int
sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
+ Attribute *a;
/* If we got a referral without a referral object, there's
* something missing that we cannot replicate. Just ignore it.
* The consumer will abort because we didn't send the expected
Debug( LDAP_DEBUG_ANY, "bogus referral in context\n",0,0,0 );
return SLAP_CB_CONTINUE;
}
- if ( !BER_BVISNULL( &srs->sr_state.ctxcsn )) {
- Attribute *a = attr_find( rs->sr_entry->e_attrs,
- slap_schema.si_ad_entryCSN );
-
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ /* Make sure entry is less than the snaphot'd contextCSN */
+ if ( ber_bvcmp( &a->a_nvals[0], &ss->ss_ctxcsn ) > 0 )
+ return LDAP_SUCCESS;
+
/* Don't send the ctx entry twice */
- if ( a && bvmatch( &a->a_nvals[0], &srs->sr_state.ctxcsn ) )
+ if ( !BER_BVISNULL( &srs->sr_state.ctxcsn ) &&
+ bvmatch( &a->a_nvals[0], &srs->sr_state.ctxcsn ) )
return LDAP_SUCCESS;
}
rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
} else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
struct berval cookie;
- slap_compose_sync_cookie( op, &cookie,
- &op->ors_filter->f_and->f_ava->aa_value,
+ slap_compose_sync_cookie( op, &cookie, &ss->ss_ctxcsn,
srs->sr_state.rid );
/* Is this a regular refresh? */
syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
slap_callback *cb;
int gotstate = 0, nochange = 0, do_present = 1;
- Filter *fand, *fava;
syncops *sop = NULL;
searchstate *ss;
sync_control *srs;
sop->s_filterstr= op->ors_filterstr;
}
- fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
- fand->f_choice = LDAP_FILTER_AND;
- fand->f_next = NULL;
- fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
- fava->f_choice = LDAP_FILTER_LE;
- fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
- fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
-#ifdef LDAP_COMP_MATCH
- fava->f_ava->aa_cf = NULL;
-#endif
- ber_dupbv_x( &fava->f_ava->aa_value, &ctxcsn, op->o_tmpmemctx );
- fand->f_and = fava;
- if ( gotstate ) {
- fava->f_next = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
- fava = fava->f_next;
+ /* If something changed, find the changes */
+ if ( gotstate && !nochange ) {
+ Filter *fand, *fava;
+
+ fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ fand->f_choice = LDAP_FILTER_AND;
+ fand->f_next = NULL;
+ fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ fand->f_and = fava;
fava->f_choice = LDAP_FILTER_GE;
fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
fava->f_ava->aa_cf = NULL;
#endif
ber_dupbv_x( &fava->f_ava->aa_value, &srs->sr_state.ctxcsn, op->o_tmpmemctx );
+ fava->f_next = op->ors_filter;
+ op->ors_filter = fand;
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
}
- fava->f_next = op->ors_filter;
- op->ors_filter = fand;
- filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
/* Let our callback add needed info to returned entries */
cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
ss->ss_on = on;
ss->ss_so = sop;
ss->ss_present = do_present;
+ ss->ss_ctxcsn.bv_len = ctxcsn.bv_len;
+ ss->ss_ctxcsn.bv_val = ss->ss_csnbuf;
+ strcpy( ss->ss_ctxcsn.bv_val, ctxcsn.bv_val );
cb->sc_response = syncprov_search_response;
cb->sc_cleanup = syncprov_search_cleanup;
cb->sc_private = ss;
sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
"DESC 'ContextCSN checkpoint interval in ops and minutes' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
- { "syncprov-sessionlog", "size", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
+ { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
"DESC 'Session log size in ops' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
if( op->o_dn.bv_len == 0 ) {
+ Statslog( LDAP_DEBUG_STATS, "%s PASSMOD\n",
+ op->o_log_prefix, 0, 0, 0, 0 );
rs->sr_text = "only authenticated users may change passwords";
return LDAP_STRONG_AUTH_REQUIRED;
}
rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id, &qpw->rs_old,
&qpw->rs_new, &rs->sr_text );
+ if ( rs->sr_err == LDAP_SUCCESS && !BER_BVISEMPTY( &id ) ) {
+ Statslog( LDAP_DEBUG_STATS, "%s PASSMOD id=\"%s\"%s%s\n",
+ op->o_log_prefix, id.bv_val,
+ qpw->rs_old.bv_val ? " old" : "",
+ qpw->rs_new.bv_val ? " new" : "", 0 );
+ } else {
+ Statslog( LDAP_DEBUG_STATS, "%s PASSMOD\n",
+ op->o_log_prefix, 0, 0, 0, 0 );
+ }
+
if ( rs->sr_err != LDAP_SUCCESS ) {
return rs->sr_err;
}
LDAP_BEGIN_DECL
+/*
+ * aci.c
+ */
+#ifdef SLAPD_ACI_ENABLED
+LDAP_SLAPD_F (int) aci_mask LDAP_P((
+ Operation *op, Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny,
+ slap_aci_scope_t scope));
+LDAP_SLAPD_F (int) OpenLDAPaciValidate LDAP_P((
+ Syntax *syn, struct berval *in ));
+LDAP_SLAPD_F (int) OpenLDAPaciPretty LDAP_P((
+ Syntax *syn, struct berval *val, struct berval *out, void *ctx ));
+LDAP_SLAPD_F (slap_mr_normalize_func) OpenLDAPaciNormalize;
+#ifdef SLAP_DYNACL
+LDAP_SLAPD_F (int) dynacl_aci_init LDAP_P(( void ));
+#endif /* SLAP_DYNACL */
+#endif /* SLAPD_ACI_ENABLED */
+
/*
* acl.c
*/
#endif /* SLAP_DYNACL */
LDAP_SLAPD_F (int) acl_init LDAP_P(( void ));
+LDAP_SLAPD_V (const struct berval) aci_bv[];
+
+LDAP_SLAPD_F (int) acl_get_part LDAP_P((
+ struct berval *list,
+ int ix,
+ char sep,
+ struct berval *bv ));
+LDAP_SLAPD_F (int) acl_match_set LDAP_P((
+ struct berval *subj,
+ Operation *op,
+ Entry *e,
+ int setref ));
+LDAP_SLAPD_F (int) acl_string_expand LDAP_P((
+ struct berval *newbuf, struct berval *pattern,
+ char *match, int nmatch, regmatch_t *matches ));
+
/*
* aclparse.c
*/
slap_ssf_t ssf,
struct berval *id ));
-LDAP_SLAPD_F (void) connection_closing LDAP_P(( Connection *c ));
+LDAP_SLAPD_F (void) connection_closing LDAP_P((
+ Connection *c, const char *why ));
LDAP_SLAPD_F (int) connection_state_closing LDAP_P(( Connection *c ));
LDAP_SLAPD_F (const char *) connection_state2str LDAP_P(( int state ))
LDAP_GCCATTR((const));
LDAP_SLAPD_F( slap_mr_indexer_func ) octetStringIndexer;
LDAP_SLAPD_F( slap_mr_filter_func ) octetStringFilter;
-
+LDAP_SLAPD_F( int ) numericoidValidate LDAP_P((
+ struct slap_syntax *syntax,
+ struct berval *in ));
/*
* schema_prep.c
err, sock_errstr(err), 0 );
if ( err != EWOULDBLOCK && err != EAGAIN ) {
- connection_closing( conn );
+ connection_closing( conn, "connection lost on write" );
ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
ldap_pvt_thread_mutex_unlock( &conn->c_write_mutex );
rs->sr_tag = req2res( op->o_tag );
rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
- send_ldap_response( op, rs );
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ Statslog( LDAP_DEBUG_STATS,
+ "%s RESULT tag=%lu err=%d text=%s\n",
+ op->o_log_prefix, rs->sr_tag, rs->sr_err,
+ rs->sr_text ? rs->sr_text : "", 0 );
+ }
}
void
rs->sr_tag = req2res( op->o_tag );
rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
- send_ldap_response( op, rs );
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ Statslog( LDAP_DEBUG_STATS,
+ "%s RESULT oid=%s err=%d text=%s\n",
+ op->o_log_prefix, rs->sr_rspoid ? rs->sr_rspoid : "",
+ rs->sr_err, rs->sr_text ? rs->sr_text : "", 0 );
+ }
}
void
rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
rs->sr_tag = LDAP_RES_INTERMEDIATE;
rs->sr_msgid = op->o_msgid;
- send_ldap_response( op, rs );
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ Statslog( LDAP_DEBUG_STATS2,
+ "%s INTERM oid=%s\n",
+ op->o_log_prefix,
+ rs->sr_rspoid ? rs->sr_rspoid : "", 0, 0, 0 );
+ }
}
int
"slap_ap_lookup: str2ad(%s): %s\n", name, text, 0 );
continue;
}
+
+ /* If it's the rootdn and a rootpw was present, we already set
+ * it so don't override it here.
+ */
+ if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values &&
+ be_isroot_dn( op->o_bd, &op->o_req_ndn ))
+ continue;
+
a = attr_find( rs->sr_entry->e_attrs, ad );
if ( !a ) continue;
if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) {
op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
- if ( op.o_bd && op.o_bd->be_search ) {
- SlapReply rs = {REP_RESULT};
- op.o_hdr = conn->c_sasl_bindop->o_hdr;
- op.o_tag = LDAP_REQ_SEARCH;
- op.o_ndn = conn->c_ndn;
- op.o_callback = &cb;
- op.o_time = slap_get_time();
- op.o_do_not_cache = 1;
- op.o_is_auth_check = 1;
- op.o_req_dn = op.o_req_ndn;
- op.ors_scope = LDAP_SCOPE_BASE;
- op.ors_deref = LDAP_DEREF_NEVER;
- op.ors_tlimit = SLAP_NO_LIMIT;
- op.ors_slimit = 1;
- op.ors_filter = &generic_filter;
- op.ors_filterstr = generic_filterstr;
- /* FIXME: we want all attributes, right? */
- op.ors_attrs = NULL;
-
- op.o_bd->be_search( &op, &rs );
+ if ( op.o_bd ) {
+ /* For rootdn, see if we can use the rootpw */
+ if ( be_isroot_dn( op.o_bd, &op.o_req_ndn ) &&
+ !BER_BVISEMPTY( &op.o_bd->be_rootpw )) {
+ struct berval cbv = BER_BVNULL;
+
+ /* If there's a recognized scheme, see if it's CLEARTEXT */
+ if ( lutil_passwd_scheme( op.o_bd->be_rootpw.bv_val )) {
+ if ( !strncasecmp( op.o_bd->be_rootpw.bv_val,
+ sc_cleartext.bv_val, sc_cleartext.bv_len )) {
+
+ /* If it's CLEARTEXT, skip past scheme spec */
+ cbv.bv_len = op.o_bd->be_rootpw.bv_len -
+ sc_cleartext.bv_len;
+ if ( cbv.bv_len ) {
+ cbv.bv_val = op.o_bd->be_rootpw.bv_val +
+ sc_cleartext.bv_len;
+ }
+ }
+ /* No scheme, use the whole value */
+ } else {
+ cbv = op.o_bd->be_rootpw;
+ }
+ if ( !BER_BVISEMPTY( &cbv )) {
+ for( i = 0; sl.list[i].name; i++ ) {
+ const char *name = sl.list[i].name;
+
+ if ( name[0] == '*' ) {
+ if ( flags & SASL_AUXPROP_AUTHZID ) continue;
+ name++;
+ } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
+ continue;
+
+ if ( !strcasecmp(name,"userPassword") ) {
+ sl.sparams->utils->prop_set( sl.sparams->propctx,
+ sl.list[i].name, cbv.bv_val, cbv.bv_len );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( op.o_bd->be_search ) {
+ SlapReply rs = {REP_RESULT};
+ op.o_hdr = conn->c_sasl_bindop->o_hdr;
+ op.o_tag = LDAP_REQ_SEARCH;
+ op.o_ndn = conn->c_ndn;
+ op.o_callback = &cb;
+ op.o_time = slap_get_time();
+ op.o_do_not_cache = 1;
+ op.o_is_auth_check = 1;
+ op.o_req_dn = op.o_req_ndn;
+ op.ors_scope = LDAP_SCOPE_BASE;
+ op.ors_deref = LDAP_DEREF_NEVER;
+ op.ors_tlimit = SLAP_NO_LIMIT;
+ op.ors_slimit = 1;
+ op.ors_filter = &generic_filter;
+ op.ors_filterstr = generic_filterstr;
+ /* FIXME: we want all attributes, right? */
+ op.ors_attrs = NULL;
+
+ op.o_bd->be_search( &op, &rs );
+ }
}
}
}
}
/* Grab the searchbase */
- assert( ludp->lud_dn );
+ assert( ludp->lud_dn != NULL );
ber_str2bv( ludp->lud_dn, 0, 0, &bv );
rc = dnValidate( NULL, &bv );
}
/* Grab the searchbase */
- assert( ludp->lud_dn );
+ assert( ludp->lud_dn != NULL );
if ( ludp->lud_dn ) {
struct berval out = BER_BVNULL;
objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
DESC 'Abstraction of a group of accounts'
- SUP top AUXILIARY
+ SUP top STRUCTURAL
MUST ( cn $ gidNumber )
MAY ( userPassword $ memberUid $ description ) )
#define HASH_Update(c,buf,len) lutil_HASHUpdate(c,buf,len)
#define HASH_Final(d,c) lutil_HASHFinal(d,c)
-#define OpenLDAPaciMatch NULL
+#define OpenLDAPaciMatch octetStringMatch
/* approx matching rules */
#define directoryStringApproxMatchOID "1.3.6.1.4.1.4203.666.4.4"
return LDAP_SUCCESS;
}
-static int
+int
numericoidValidate(
Syntax *syntax,
struct berval *in )
/* OpenLDAP Experimental Syntaxes */
{"( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
SLAP_SYNTAX_HIDE,
- UTF8StringValidate /* THIS WILL CHANGE FOR NEW ACI SYNTAX */,
- NULL},
+ OpenLDAPaciValidate,
+ OpenLDAPaciPretty},
#endif
#ifdef SLAPD_AUTHPASSWD
{"( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
"SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
- NULL, NULL, OpenLDAPaciMatch,
+ NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
NULL, NULL,
NULL},
#endif
#endif
AttributeDescription *si_ad_description;
AttributeDescription *si_ad_seeAlso;
-
+
/* Undefined Attribute Type */
AttributeType *si_at_undefined;
#define ACL_STATE_INIT { ACL_STATE_NOT_RECORDED, NULL, NULL, 0UL, \
{ { 0, 0 } }, 0, NULL, 0, 0, NULL }
+#ifdef SLAPD_ACI_ENABLED
+typedef enum slap_aci_scope_t {
+ SLAP_ACI_SCOPE_ENTRY = 0x1,
+ SLAP_ACI_SCOPE_CHILDREN = 0x2,
+ SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
+} slap_aci_scope_t;
+#endif /* SLAPD_ACI_ENABLED */
+
+enum {
+ ACI_BV_ENTRY,
+ ACI_BV_CHILDREN,
+ ACI_BV_ONELEVEL,
+ ACI_BV_SUBTREE,
+ ACI_BV_BR_ENTRY,
+ ACI_BV_BR_ALL,
+ ACI_BV_ACCESS_ID,
+#if 0
+ ACI_BV_ANONYMOUS = BER_BVC("anonymous"),
+#endif
+ ACI_BV_PUBLIC,
+ ACI_BV_USERS,
+ ACI_BV_SELF,
+ ACI_BV_DNATTR,
+ ACI_BV_GROUP,
+ ACI_BV_ROLE,
+ ACI_BV_SET,
+ ACI_BV_SET_REF,
+ ACI_BV_GRANT,
+ ACI_BV_DENY,
+
+ ACI_BV_IP_EQ,
+#ifdef LDAP_PF_LOCAL
+ ACI_BV_PATH_EQ,
+#if 0
+ ACI_BV_DIRSEP,
+#endif
+#endif /* LDAP_PF_LOCAL */
+
+ ACI_BV_GROUP_CLASS,
+ ACI_BV_GROUP_ATTR,
+ ACI_BV_ROLE_CLASS,
+ ACI_BV_ROLE_ATTR,
+ ACI_BV_SET_ATTR,
+
+ ACI_BV_LAST
+};
+
/*
* Backend-info
* represents a backend
int c_struct_state; /* structure management state */
int c_conn_state; /* connection state */
int c_conn_idx; /* slot in connections array */
+ const char *c_close_reason; /* why connection is closing */
ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
Sockbuf *c_sb; /* ber connection stuff */
case SLAPI_X_GROUP_ATTRIBUTE:
case SLAPI_X_GROUP_OPERATION_DN:
case SLAPI_X_GROUP_TARGET_ENTRY:
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
case SLAPI_PLUGIN_AUDIT_DATA:
case SLAPI_IBM_PBLOCK:
case SLAPI_PLUGIN_VERSION:
PBLOCK_ASSERT_OP( pb, 0 );
*((int *)value) = get_no_schema_check( pb->pb_op );
break;
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_ADD ) {
+ struct berval tmpval = BER_BVNULL;
+
+ rc = mods_structural_class( pb->pb_op->ora_modlist,
+ &tmpval, &pb->pb_rs->sr_text,
+ pb->pb_textbuf, sizeof( pb->pb_textbuf ));
+ *((char **)value) = tmpval.bv_val;
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
case SLAPI_REQCONTROLS:
PBLOCK_ASSERT_OP( pb, 0 );
*((LDAPControl ***)value) = pb->pb_op->o_ctrls;
case SLAPI_X_CONN_CLIENTPATH:
case SLAPI_CONN_SERVERIP:
case SLAPI_X_CONN_SERVERPATH:
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
/* These parameters cannot be set */
rc = PBLOCK_ERROR;
break;
{
int rc;
+ Statslog( LDAP_DEBUG_STATS, "%s STARTTLS\n",
+ op->o_log_prefix, 0, 0, 0, 0 );
+
if ( op->ore_reqdata != NULL ) {
/* no request data should be provided */
rs->sr_text = "no request data expected";
rc = ldap_bind_s( ld, manager, passwd, LDAP_AUTH_SIMPLE );
if ( rc != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_bind" );
- if ( rc == LDAP_BUSY && do_retry > 0 ) {
- do_retry--;
- goto retry;
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry == 1 ) {
+ do_retry = 0;
+ sleep( 1 );
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
}
exit( EXIT_FAILURE );
}
rc = ldap_bind_s( ld, manager, passwd, LDAP_AUTH_SIMPLE );
if ( rc != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_bind" );
- if ( rc == LDAP_BUSY && do_retry > 0 ) {
- do_retry--;
- goto retry;
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry == 1 ) {
+ do_retry = 0;
+ sleep( 1 );
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
}
exit( EXIT_FAILURE );
}
rc = ldap_bind_s( ld, manager, passwd, LDAP_AUTH_SIMPLE );
if ( rc != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_bind" );
- if ( rc == LDAP_BUSY && do_retry > 0 ) {
- do_retry--;
- goto retry;
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry == 1 ) {
+ do_retry = 0;
+ sleep( 1 );
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
}
exit( EXIT_FAILURE );
}
rc = ldap_bind_s( ld, NULL, NULL, LDAP_AUTH_SIMPLE );
if ( rc != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_bind" );
- if ( rc == LDAP_BUSY && do_retry > 0 ) {
- do_retry--;
- goto retry;
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry == 1 ) {
+ do_retry = 0;
+ sleep( 1 );
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
}
exit( EXIT_FAILURE );
}
rc = ldap_bind_s( ld, manager, passwd, LDAP_AUTH_SIMPLE );
if ( rc != LDAP_SUCCESS ) {
- if ( rc == LDAP_BUSY && do_retry == 1 ) {
- do_retry = 0;
- goto retry;
- }
ldap_perror( ld, "ldap_bind" );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ sleep( 1 );
+ goto retry;
+ }
+ /* fallthru */
+ default:
+ break;
+ }
exit( EXIT_FAILURE );
}
filter, attrs, 0, &res );
if ( rc != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_search" );
- if ( rc == LDAP_BUSY && do_retry == 1 ) {
- do_retry = 0;
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ do_retry--;
goto retry;
}
if ( rc != LDAP_NO_SUCH_OBJECT ) break;
static void
usage( char *name )
{
- fprintf( stderr, "usage: %s -H <uri> | ([-h <host>] -p <port>) -D <manager> -w <passwd> -d <datadir> [-j <maxchild>] [-l <loops>] -P <progdir>\n", name );
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> | ([-h <host>] -p <port>) "
+ "-D <manager> "
+ "-w <passwd> "
+ "-d <datadir> "
+ "[-j <maxchild>] "
+ "[-l <loops>] "
+ "-P <progdir> "
+ "[-r <maxretries>]\n",
+ name );
exit( EXIT_FAILURE );
}