diff --git a/contrib/slapd-modules/acl/README b/contrib/slapd-modules/acl/README new file mode 100644 index 0000000000..b623ab9acc --- /dev/null +++ b/contrib/slapd-modules/acl/README @@ -0,0 +1,36 @@ +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 + by dynacl/posixGroup[.{exact,expand}]= {|" results from +the expansion of submatches in the "" portion. "|" +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. + diff --git a/contrib/slapd-modules/acl/posixgroup.c b/contrib/slapd-modules/acl/posixgroup.c new file mode 100644 index 0000000000..3f06b7fd58 --- /dev/null +++ b/contrib/slapd-modules/acl/posixgroup.c @@ -0,0 +1,320 @@ +/* $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 + * . + */ + +#include + +#include +#include +#include + +/* 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 */ diff --git a/doc/man/man5/slapo-syncprov.5 b/doc/man/man5/slapo-syncprov.5 index 6b12b4e7ea..02cf233740 100644 --- a/doc/man/man5/slapo-syncprov.5 +++ b/doc/man/man5/slapo-syncprov.5 @@ -37,22 +37,19 @@ directive. .TP .B syncprov-checkpoint After a write operation has succeeded, write the contextCSN to the underlying -database if write operations or more than time have passed +database if +.B +write operations or more than +.B +time have passed since the last checkpoint. Checkpointing is disabled by default. .TP -.B syncprov-sessionlog -Specify a session log for recording information about entries that have been -scoped out of the content identified by -.BR . -The number of entries in the log is limited by -.BR . -Both -.B -and -.B -must be non-negative integers, and -.B -can have no more than three decimal digits. +.B syncprov-sessionlog +Specify a session log for recording information about write operations made +on the database. The +.B +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 diff --git a/doc/man/man5/slapo-unique.5 b/doc/man/man5/slapo-unique.5 index ae30ff1a20..4ad9dc9162 100644 --- a/doc/man/man5/slapo-unique.5 +++ b/doc/man/man5/slapo-unique.5 @@ -58,7 +58,7 @@ attributes, as these will generally not be unique, nor are they operational attributes. .TP .B unique_attributes -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 ) @@ -79,7 +79,15 @@ on the 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 diff --git a/doc/man/man8/slapcat.8 b/doc/man/man8/slapcat.8 index 1609dbf821..d6eb2ff77f 100644 --- a/doc/man/man8/slapcat.8 +++ b/doc/man/man8/slapcat.8 @@ -62,7 +62,14 @@ cannot be used in conjunction with the 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. diff --git a/include/slapi-plugin.h b/include/slapi-plugin.h index 8344cf766a..db381da322 100644 --- a/include/slapi-plugin.h +++ b/include/slapi-plugin.h @@ -530,6 +530,7 @@ int slapi_x_backend_get_flags( const Slapi_Backend *be, unsigned long *flags ); #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" diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c index 2c1be570f6..ecfb3808b1 100644 --- a/libraries/libldap/url.c +++ b/libraries/libldap/url.c @@ -1228,14 +1228,9 @@ ldap_url_duplist (LDAPURLDesc *ludlist) 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; @@ -1254,7 +1249,7 @@ ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep 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); @@ -1268,6 +1263,18 @@ ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep 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, diff --git a/libraries/librewrite/rewrite-int.h b/libraries/librewrite/rewrite-int.h index 4b5a60bee2..fb8dbf6ee4 100644 --- a/libraries/librewrite/rewrite-int.h +++ b/libraries/librewrite/rewrite-int.h @@ -206,10 +206,7 @@ struct rewrite_rule { 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 diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c index 9be1d844cb..84ff22b417 100644 --- a/libraries/librewrite/rule.c +++ b/libraries/librewrite/rule.c @@ -346,14 +346,6 @@ rewrite_rule_compile( 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 ... */ @@ -427,13 +419,7 @@ recurse:; 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 ); @@ -503,9 +489,6 @@ rewrite_rule_destroy( } 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; diff --git a/servers/slapd/Makefile.in b/servers/slapd/Makefile.in index 2ae075d17b..f774b1322c 100644 --- a/servers/slapd/Makefile.in +++ b/servers/slapd/Makefile.in @@ -38,6 +38,7 @@ SRCS = main.c globals.c bconfig.c config.c daemon.c \ 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 \ @@ -55,6 +56,7 @@ 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. diff --git a/servers/slapd/abandon.c b/servers/slapd/abandon.c index df2f937f49..006a8c8f39 100644 --- a/servers/slapd/abandon.c +++ b/servers/slapd/abandon.c @@ -51,6 +51,9 @@ do_abandon( Operation *op, SlapReply *rs ) 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; diff --git a/servers/slapd/aci.c b/servers/slapd/aci.c new file mode 100644 index 0000000000..3d587cd9e6 --- /dev/null +++ b/servers/slapd/aci.c @@ -0,0 +1,1502 @@ +/* aci.c - routines to parse and check acl's */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* 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 + +#include +#include +#include +#include +#include + +#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 part of an aci's + * rights list is of the form =, + * that means the aci applies only to attrs with + * the given value. Furthermore, if the attr is + * of the form =*, then 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. + + = {entry|children|subtree} + = {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 */ + diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index d1801f898d..2f27cf5b5f 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -42,47 +42,43 @@ /* * 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"), + 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"), - aci_bv_ip_eq = BER_BVC("IP="), + 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); + BER_BVC(SLAPD_GROUP_CLASS), + BER_BVC(SLAPD_GROUP_ATTR), + BER_BVC(SLAPD_ROLE_CLASS), + BER_BVC(SLAPD_ROLE_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_ACI_SET_ATTR) +}; static AccessControl * slap_acl_get( AccessControl *ac, int *count, @@ -102,35 +98,17 @@ static slap_control_t slap_acl_mask( 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 @@ -1148,7 +1126,7 @@ acl_mask_dn( 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 ) ) { @@ -1387,14 +1365,15 @@ slap_acl_mask( 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 ); @@ -1506,7 +1485,7 @@ slap_acl_mask( 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; @@ -1551,7 +1530,7 @@ slap_acl_mask( 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; @@ -1608,7 +1587,7 @@ slap_acl_mask( 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; @@ -1627,11 +1606,12 @@ slap_acl_mask( 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 ) { @@ -1670,11 +1650,14 @@ slap_acl_mask( 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; @@ -1709,7 +1692,7 @@ slap_acl_mask( 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; @@ -1817,7 +1800,7 @@ slap_acl_mask( 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 ) ) { @@ -1902,7 +1885,7 @@ slap_acl_mask( 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 ) ) { @@ -1913,7 +1896,7 @@ slap_acl_mask( bv = b->a_set_pat; } - if ( aci_match_set( &bv, op, e, 0 ) == 0 ) { + if ( acl_match_set( &bv, op, e, 0 ) == 0 ) { continue; } } @@ -1973,7 +1956,7 @@ slap_acl_mask( /* 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; } @@ -1982,7 +1965,11 @@ slap_acl_mask( 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 ); @@ -2067,11 +2054,11 @@ slap_acl_mask( * 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; @@ -2098,13 +2085,13 @@ slap_acl_mask( 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 ) { @@ -2213,6 +2200,8 @@ slap_acl_mask( *mask = modmask; } + a2pmask = *mask; + Debug( LDAP_DEBUG_ACL, "<= acl_mask: [%d] mask: %s\n", i, accessmask2str(*mask, accessmaskbuf, 1), 0 ); @@ -2382,8 +2371,8 @@ done: return( ret ); } -static int -aci_get_part( +int +acl_get_part( struct berval *list, int ix, char sep, @@ -2425,15 +2414,15 @@ aci_get_part( 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 ]; @@ -2476,17 +2465,17 @@ aci_set_cb_gather( Operation *op, SlapReply *rs ) } 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=*)" ); @@ -2495,7 +2484,7 @@ aci_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *de * 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 ); @@ -2616,9 +2605,9 @@ url_done:; } 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; @@ -2649,17 +2638,16 @@ aci_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *d 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 ); @@ -2672,12 +2660,12 @@ aci_match_set ( 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 ]; } /* @@ -2707,7 +2695,7 @@ aci_match_set ( 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 ); } @@ -2715,698 +2703,6 @@ aci_match_set ( 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 part of an aci's - * rights list is of the form =, - * that means the aci applies only to attrs with - * the given value. Furthermore, if the attr is - * of the form =*, then 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. - - = {entry|children|subtree} - = {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 /* @@ -3468,27 +2764,22 @@ int 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, @@ -3572,8 +2863,8 @@ string_expand( *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; } @@ -3599,7 +2890,7 @@ regex_matches( 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]; diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c index 30a4b455f6..5c124830d6 100644 --- a/servers/slapd/aclparse.c +++ b/servers/slapd/aclparse.c @@ -1109,7 +1109,7 @@ parse_acl( } } 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, @@ -2250,6 +2250,20 @@ access_free( Access *a ) 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 ); } @@ -2263,6 +2277,9 @@ acl_free( AccessControl *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 ) { @@ -2523,8 +2540,9 @@ access2text( Access *b, char *ptr ) 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 ); } diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index a7525be1d8..12d7ea3eb6 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -850,6 +850,12 @@ fetch_entry_retry: 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; } diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c index d9e395d3ed..09b23924d5 100644 --- a/servers/slapd/back-ldap/bind.c +++ b/servers/slapd/back-ldap/bind.c @@ -251,12 +251,150 @@ ldap_back_freeconn( Operation *op, struct ldapconn *lc ) return 0; } +#ifdef HAVE_TLS +static int +ldap_back_start_tls( + LDAP *ld, + int protocol, + int *is_tls, + const char *url, + unsigned flags, + const char **text ) +{ + int rc = LDAP_SUCCESS; + struct ldapinfo li; + + /* this is ridicolous... */ + li.flags = flags; + + /* start TLS ("tls-[try-]{start,propagate}" statements) */ + if ( ( LDAP_BACK_USE_TLS( &li ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &li ) ) ) + && !ldap_is_ldaps_url( url ) ) + { +#ifdef SLAP_STARTTLS_ASYNCHRONOUS + /* + * use asynchronous StartTLS + * in case, chase referral (not implemented yet) + */ + int msgid; + + 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 retries = 1; + struct timeval tv = { 0, 0 }; + +retry:; + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + rc = LDAP_OTHER; + + } else if ( rc == 0 ) { + if ( retries ) { + retries--; + tv.tv_sec = 0; + tv.tv_usec = 100000; + goto retry; + } + rc = LDAP_OTHER; + + } else if ( rc == LDAP_RES_EXTENDED ) { + struct berval *data = NULL; + + rc = ldap_parse_extended_result( ld, res, + NULL, &data, 0 ); + 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 ( rc == LDAP_SUCCESS ) { + rc = ldap_install_tls( ld ); + + } else if ( rc == LDAP_REFERRAL ) { + rc = LDAP_OTHER; + *text = "unwilling to chase referral returned by Start TLS exop"; + } + + if ( data ) { + if ( data->bv_val ) { + ber_memfree( data->bv_val ); + } + ber_memfree( data ); + } + } + + } else { + rc = LDAP_OTHER; + } + + if ( res != NULL ) { + ldap_msgfree( res ); + } + } +#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */ + /* + * use synchronous StartTLS + */ + 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 */ + 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; + } + + } 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 ); @@ -276,102 +414,24 @@ ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, lda } #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 ) ) - { -#ifdef SLAP_STARTTLS_ASYNCHRONOUS - /* - * use asynchronous StartTLS - * in case, chase referral (not implemented yet) - */ - int msgid; - - rs->sr_err = ldap_start_tls( ld, NULL, NULL, &msgid ); - if ( rs->sr_err == LDAP_SUCCESS ) { - LDAPMessage *res = NULL; - int rc, 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; - - } else if ( rc == 0 ) { - if ( retries ) { - retries--; - tv.tv_sec = 0; - tv.tv_usec = 100000; - goto retry; - } - rs->sr_err = LDAP_OTHER; - - } else if ( rc == LDAP_RES_EXTENDED ) { - struct berval *data = NULL; - - rs->sr_err = ldap_parse_extended_result( ld, res, - NULL, &data, 0 ); - if ( rs->sr_err == LDAP_SUCCESS ) { - rs->sr_err = 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 ); - - } else if ( rs->sr_err == LDAP_REFERRAL ) { - rs->sr_err = LDAP_OTHER; - rs->sr_text = "unwilling to chase referral returned by Start TLS exop"; - } - - if ( data ) { - if ( data->bv_val ) { - ber_memfree( data->bv_val ); - } - ber_memfree( data ); - } - } - - } else { - rs->sr_err = LDAP_OTHER; - } - - if ( res != NULL ) { - ldap_msgfree( res ); - } - } -#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */ - /* - * use synchronous StartTLS - */ - rs->sr_err = 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; - } - - /* in case Start TLS is not critical */ - rs->sr_err = LDAP_SUCCESS; + 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 ) { @@ -407,14 +467,28 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok ) 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 { @@ -454,6 +528,33 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok ) } } +#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 */ @@ -489,7 +590,8 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok ) "=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n", (void *)lc, refcnt, 0 ); } - + +done:; return lc; } @@ -673,6 +775,22 @@ ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request, { 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, diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c index 7a8af5776a..7968dc5331 100644 --- a/servers/slapd/back-ldap/config.c +++ b/servers/slapd/back-ldap/config.c @@ -908,8 +908,22 @@ ldap_back_cf_gen( ConfigArgs *c ) 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 \": " + "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; @@ -1562,6 +1576,9 @@ ldap_back_exop_whoami( 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; diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c index 85c334f9c9..ff9425bb57 100644 --- a/servers/slapd/back-ldap/search.c +++ b/servers/slapd/back-ldap/search.c @@ -394,6 +394,15 @@ fail:; 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 ); diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h index daf6e85df9..df9bfdf023 100644 --- a/servers/slapd/back-meta/back-meta.h +++ b/servers/slapd/back-meta/back-meta.h @@ -203,6 +203,7 @@ enum { typedef struct metatarget_t { char *mt_uri; + int mt_scope; struct berval mt_psuffix; /* pretty suffix */ struct berval mt_nsuffix; /* normalized suffix */ @@ -351,6 +352,7 @@ meta_back_conn_dup( extern int meta_back_is_candidate( struct berval *nsuffix, + int suffixscope, struct berval *ndn, int scope ); diff --git a/servers/slapd/back-meta/bind.c b/servers/slapd/back-meta/bind.c index f7bc68ff87..e211f63631 100644 --- a/servers/slapd/back-meta/bind.c +++ b/servers/slapd/back-meta/bind.c @@ -502,15 +502,22 @@ meta_back_dobind( { 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 @@ -546,8 +553,23 @@ meta_back_dobind( 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 ); diff --git a/servers/slapd/back-meta/candidates.c b/servers/slapd/back-meta/candidates.c index a59606ff70..aa8c7628df 100644 --- a/servers/slapd/back-meta/candidates.c +++ b/servers/slapd/back-meta/candidates.c @@ -23,6 +23,7 @@ #include "portable.h" #include +#include "ac/string.h" #include "slap.h" #include "../back-ldap/back-ldap.h" @@ -59,11 +60,46 @@ 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 ) ) { @@ -76,28 +112,6 @@ meta_back_is_candidate( 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 * @@ -114,7 +128,9 @@ 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; diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c index 24f357ed5e..996161dded 100644 --- a/servers/slapd/back-meta/config.c +++ b/servers/slapd/back-meta/config.c @@ -159,9 +159,7 @@ meta_back_db_config( /* * 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 ) { @@ -173,6 +171,25 @@ meta_back_db_config( 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' ) { @@ -183,10 +200,6 @@ meta_back_db_config( 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 ); diff --git a/servers/slapd/back-meta/conn.c b/servers/slapd/back-meta/conn.c index 422107d78d..5f10ce40d2 100644 --- a/servers/slapd/back-meta/conn.c +++ b/servers/slapd/back-meta/conn.c @@ -526,6 +526,7 @@ meta_back_get_candidate( * 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; @@ -538,6 +539,9 @@ meta_back_get_candidate( } break; } + + } else { + rs->sr_err = LDAP_SUCCESS; } return candidate; @@ -795,6 +799,13 @@ meta_back_getconn( } 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; @@ -803,18 +814,27 @@ meta_back_getconn( 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; } @@ -894,6 +914,7 @@ meta_back_getconn( 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 ) ) { diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c index 30e0d9ba22..8437cea8e8 100644 --- a/servers/slapd/back-meta/search.c +++ b/servers/slapd/back-meta/search.c @@ -97,6 +97,11 @@ meta_back_search_start( &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 { /* @@ -124,7 +129,11 @@ meta_back_search_start( 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 */ { @@ -693,17 +702,52 @@ really_bad:; * * 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 @@ -742,8 +786,6 @@ really_bad:; 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; @@ -751,7 +793,7 @@ really_bad:; finish:; if ( matched ) { - free( matched ); + op->o_tmpfree( matched, op->o_tmpmemctx ); } if ( rs->sr_v2ref ) { @@ -786,7 +828,7 @@ finish:; meta_back_release_conn( op, mc ); - return rc; + return rs->sr_err; } static int diff --git a/servers/slapd/cancel.c b/servers/slapd/cancel.c index c1b288c6d5..16289a9d47 100644 --- a/servers/slapd/cancel.c +++ b/servers/slapd/cancel.c @@ -60,6 +60,9 @@ int cancel_extop( Operation *op, SlapReply *rs ) 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 ) { diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 56c02c2f7a..217ba8da08 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -50,6 +50,8 @@ static Connection *connections = NULL; 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 @@ -191,7 +193,7 @@ int connections_shutdown(void) 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 ); @@ -222,7 +224,7 @@ int connections_timeout_idle(time_t now) if( difftime( c->c_activitytime+global_idletimeout, now) < 0 ) { /* close it */ - connection_closing( c ); + connection_closing( c, "idletimeout" ); connection_close( c ); i++; } @@ -492,6 +494,7 @@ long connection_init( 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 ); @@ -563,6 +566,7 @@ long connection_init( 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; @@ -625,6 +629,7 @@ connection_destroy( Connection *c ) /* 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 ); @@ -635,6 +640,7 @@ connection_destroy( Connection *c ) /* 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); @@ -672,9 +678,10 @@ connection_destroy( Connection *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 ); @@ -688,6 +695,7 @@ connection_destroy( Connection *c ) 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 */ @@ -740,7 +748,7 @@ static void connection_abandon( Connection *c ) } } -void connection_closing( Connection *c ) +void connection_closing( Connection *c, const char *why ) { assert( connections != NULL ); assert( c != NULL ); @@ -758,6 +766,7 @@ void connection_closing( Connection *c ) 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 ); @@ -773,6 +782,9 @@ void connection_closing( Connection *c ) 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; } } @@ -1085,7 +1097,8 @@ operations_error: 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: @@ -1144,6 +1157,7 @@ void connection_client_stop( 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 ); } @@ -1209,7 +1223,7 @@ int connection_read(ber_socket_t s) 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 @@ -1247,6 +1261,9 @@ int connection_read(ber_socket_t s) "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 ); } @@ -1281,7 +1298,7 @@ int connection_read(ber_socket_t s) "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 ); @@ -1310,7 +1327,7 @@ int connection_read(ber_socket_t s) "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 ); diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c index 24bc185ff5..a47d1c418f 100644 --- a/servers/slapd/dn.c +++ b/servers/slapd/dn.c @@ -996,7 +996,7 @@ dnRelativeMatch( asserted->bv_val, asserted->bv_len ); } else { - return 1; + match = 1; } } @@ -1022,7 +1022,7 @@ dnRelativeMatch( asserted->bv_val, asserted->bv_len ); } else { - return 1; + match = 1; } } @@ -1049,7 +1049,7 @@ dnRelativeMatch( match = dnIsOneLevelRDN( &rdn ) ? 0 : 1; } } else { - return 1; + match = 1; } } diff --git a/servers/slapd/extended.c b/servers/slapd/extended.c index 36c42e446e..33a8a11807 100644 --- a/servers/slapd/extended.c +++ b/servers/slapd/extended.c @@ -166,6 +166,8 @@ do_extended( /* 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" ); @@ -196,6 +198,8 @@ fe_extended( Operation *op, SlapReply *rs ) 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, @@ -328,6 +332,9 @@ whoami_extop ( 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 ) { diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index 64daabd22c..503a9c63fa 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -998,9 +998,9 @@ ppolicy_bind( Operation *op, SlapReply *rs ) /* 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; } @@ -1785,11 +1785,11 @@ int ppolicy_init() 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 ); } diff --git a/servers/slapd/overlays/syncprov.c b/servers/slapd/overlays/syncprov.c index bc8d940615..4e6ebec2a7 100644 --- a/servers/slapd/overlays/syncprov.c +++ b/servers/slapd/overlays/syncprov.c @@ -447,8 +447,8 @@ syncprov_findbase( Operation *op, fbase_cookie *fc ) * 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 @@ -555,6 +555,7 @@ syncprov_findcsn( Operation *op, int mode ) 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]; @@ -579,6 +580,7 @@ syncprov_findcsn( Operation *op, int mode ) fop.ors_filter = &cf; fop.ors_filterstr.bv_val = buf; +again: switch( mode ) { case FIND_MAXCSN: cf.f_choice = LDAP_FILTER_GE; @@ -595,10 +597,18 @@ syncprov_findcsn( Operation *op, int mode ) 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; @@ -646,7 +656,14 @@ syncprov_findcsn( Operation *op, int mode ) 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 ); @@ -1592,6 +1609,8 @@ typedef struct searchstate { slap_overinst *ss_on; syncops *ss_so; int ss_present; + struct berval ss_ctxcsn; + char ss_csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE]; } searchstate; static int @@ -1690,6 +1709,7 @@ syncprov_search_response( Operation *op, SlapReply *rs ) 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 @@ -1700,12 +1720,15 @@ syncprov_search_response( Operation *op, SlapReply *rs ) 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, @@ -1716,8 +1739,7 @@ syncprov_search_response( Operation *op, SlapReply *rs ) } 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? */ @@ -1763,7 +1785,6 @@ syncprov_op_search( Operation *op, SlapReply *rs ) 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; @@ -1887,21 +1908,15 @@ shortcut: 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; @@ -1909,10 +1924,10 @@ shortcut: 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); @@ -1920,6 +1935,9 @@ shortcut: 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; @@ -2000,7 +2018,7 @@ static ConfigTable spcfg[] = { 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 }, diff --git a/servers/slapd/passwd.c b/servers/slapd/passwd.c index 738d9add84..3d63e447fe 100644 --- a/servers/slapd/passwd.c +++ b/servers/slapd/passwd.c @@ -62,6 +62,8 @@ int passwd_extop( 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; } @@ -74,6 +76,16 @@ int passwd_extop( 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; } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index d25d1f3e23..08b9668d32 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -31,6 +31,30 @@ 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 */ @@ -71,6 +95,22 @@ LDAP_SLAPD_F (slap_dynacl_t *) slap_dynacl_get LDAP_P(( const char *name )); #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 */ @@ -577,7 +617,8 @@ LDAP_SLAPD_F (long) connection_init LDAP_P(( 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)); @@ -1406,7 +1447,9 @@ LDAP_SLAPD_F (void) schema_destroy LDAP_P(( void )); 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 diff --git a/servers/slapd/result.c b/servers/slapd/result.c index d1e79bf339..1593e22bb0 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -189,7 +189,7 @@ static long send_ldap_ber( 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 ); @@ -613,7 +613,12 @@ send_ldap_sasl( Operation *op, SlapReply *rs ) 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 @@ -630,7 +635,12 @@ slap_send_ldap_extended( Operation *op, SlapReply *rs ) 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 @@ -644,7 +654,12 @@ slap_send_ldap_intermediate( Operation *op, SlapReply *rs ) 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 diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c index e92780132f..66c591a956 100644 --- a/servers/slapd/sasl.c +++ b/servers/slapd/sasl.c @@ -202,6 +202,14 @@ sasl_ap_lookup( Operation *op, SlapReply *rs ) "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 ) ) { @@ -318,26 +326,69 @@ slap_auxprop_lookup( 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; + 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; - op.o_bd->be_search( &op, &rs ); + /* 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 ); + } } } } diff --git a/servers/slapd/saslauthz.c b/servers/slapd/saslauthz.c index 3e28af1fd6..335ae63843 100644 --- a/servers/slapd/saslauthz.c +++ b/servers/slapd/saslauthz.c @@ -452,7 +452,7 @@ is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); } /* Grab the searchbase */ - assert( ludp->lud_dn ); + assert( ludp->lud_dn != NULL ); ber_str2bv( ludp->lud_dn, 0, 0, &bv ); rc = dnValidate( NULL, &bv ); @@ -830,7 +830,7 @@ is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val ); } /* Grab the searchbase */ - assert( ludp->lud_dn ); + assert( ludp->lud_dn != NULL ); if ( ludp->lud_dn ) { struct berval out = BER_BVNULL; diff --git a/servers/slapd/schema/nis.schema b/servers/slapd/schema/nis.schema index d2c3798b41..1f1524ec13 100644 --- a/servers/slapd/schema/nis.schema +++ b/servers/slapd/schema/nis.schema @@ -172,7 +172,7 @@ objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' 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 ) ) diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c index 344acbe974..183d1875bf 100644 --- a/servers/slapd/schema_init.c +++ b/servers/slapd/schema_init.c @@ -50,7 +50,7 @@ #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" @@ -1861,7 +1861,7 @@ telephoneNumberNormalize( return LDAP_SUCCESS; } -static int +int numericoidValidate( Syntax *syntax, struct berval *in ) @@ -3427,8 +3427,8 @@ static slap_syntax_defs_rec syntax_defs[] = { /* 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 @@ -3851,7 +3851,7 @@ static slap_mrule_defs_rec mrule_defs[] = { {"( 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 diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index f1d55cab36..0d9fc9ce9d 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -914,7 +914,7 @@ struct slap_internal_schema { #endif AttributeDescription *si_ad_description; AttributeDescription *si_ad_seeAlso; - + /* Undefined Attribute Type */ AttributeType *si_at_undefined; @@ -1481,6 +1481,53 @@ typedef struct slap_acl_state { #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 @@ -2535,6 +2582,7 @@ typedef struct slap_conn { 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 */ diff --git a/servers/slapd/slapi/slapi_pblock.c b/servers/slapd/slapi/slapi_pblock.c index 1b4e5dab56..fa99303b0a 100644 --- a/servers/slapd/slapi/slapi_pblock.c +++ b/servers/slapd/slapi/slapi_pblock.c @@ -277,6 +277,7 @@ pblock_get_param_class( int param ) 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: @@ -472,6 +473,20 @@ pblock_get( Slapi_PBlock *pb, int param, void **value ) 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; @@ -1203,6 +1218,7 @@ pblock_set( Slapi_PBlock *pb, int param, void *value ) 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; diff --git a/servers/slapd/starttls.c b/servers/slapd/starttls.c index 8864b03dd6..2ce38146c6 100644 --- a/servers/slapd/starttls.c +++ b/servers/slapd/starttls.c @@ -27,6 +27,9 @@ starttls_extop ( Operation *op, SlapReply *rs ) { 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"; diff --git a/tests/progs/slapd-addel.c b/tests/progs/slapd-addel.c index c96cdccd35..6b8caacc06 100644 --- a/tests/progs/slapd-addel.c +++ b/tests/progs/slapd-addel.c @@ -291,9 +291,17 @@ retry:; 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 ); } diff --git a/tests/progs/slapd-modify.c b/tests/progs/slapd-modify.c index 37e45ec19e..b639df5539 100644 --- a/tests/progs/slapd-modify.c +++ b/tests/progs/slapd-modify.c @@ -185,9 +185,17 @@ retry:; 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 ); } diff --git a/tests/progs/slapd-modrdn.c b/tests/progs/slapd-modrdn.c index 60e85fa77b..2e95ddfa03 100644 --- a/tests/progs/slapd-modrdn.c +++ b/tests/progs/slapd-modrdn.c @@ -177,9 +177,17 @@ retry:; 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 ); } diff --git a/tests/progs/slapd-read.c b/tests/progs/slapd-read.c index c3c09758b5..e2c0768679 100644 --- a/tests/progs/slapd-read.c +++ b/tests/progs/slapd-read.c @@ -140,9 +140,17 @@ retry:; 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 ); } diff --git a/tests/progs/slapd-search.c b/tests/progs/slapd-search.c index de42211fca..7158831056 100644 --- a/tests/progs/slapd-search.c +++ b/tests/progs/slapd-search.c @@ -157,11 +157,19 @@ retry:; 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 ); } @@ -172,8 +180,8 @@ retry:; 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; diff --git a/tests/progs/slapd-tester.c b/tests/progs/slapd-tester.c index 7d14de72c6..4121b4d174 100644 --- a/tests/progs/slapd-tester.c +++ b/tests/progs/slapd-tester.c @@ -71,7 +71,17 @@ static char argbuf[BUFSIZ]; static void usage( char *name ) { - fprintf( stderr, "usage: %s -H | ([-h ] -p ) -D -w -d [-j ] [-l ] -P \n", name ); + fprintf( stderr, + "usage: %s " + "-H | ([-h ] -p ) " + "-D " + "-w " + "-d " + "[-j ] " + "[-l ] " + "-P " + "[-r ]\n", + name ); exit( EXIT_FAILURE ); }