Merge branch 'its6198' into 'master'

Draft: ITS#6198 A take on extension ACLs

See merge request openldap/openldap!720
This commit is contained in:
Ondřej Kuzník 2026-02-02 20:11:10 +00:00
commit 6447b21fdd
10 changed files with 308 additions and 77 deletions

View file

@ -154,6 +154,8 @@ It can have the forms
dn[.<dnstyle>]=<dnpattern>
filter=<ldapfilter>
attrs=<attrlist>[ val[/matchingRule][.<attrstyle>]=<attrval>]
op=<operation>|<oid>
control=<oid>
.fi
.LP
with
@ -275,10 +277,26 @@ or
.BR children ,
resulting in base, onelevel, subtree or children match, respectively.
.LP
The dn, filter, and attrs statements are additive; they can be used in sequence
to select entities the access rule applies to based on naming context,
value and attribute type simultaneously.
Submatches resulting from
The statement
.B op=<operation>|<oid>
narrows the rule to a specific operation (e.g. search, rename) or, if
.B oid
is provided, a specific extended operation.
.B objectIdentifier macros
are also resolved but keep in mind that those as currently case-sensitive.
.LP
The statement
.B control=<oid>
narrows the rule to when a specific control has been requested. Same as with
the
.B op=<oid>
case, you can make use of
.B objectIdentifier macros
from the schema.
.LP
The dn, filter, attrs, op and control statements are additive; they can be used
in sequence to select entities the access rule applies to based on naming
context, value and attribute type simultaneously. Submatches resulting from
.B regex
matching can be dereferenced in the
.B <who>

View file

@ -545,6 +545,19 @@ slap_acl_get(
if ( a != frontendDB->be_acl && state->as_fe_done )
state->as_fe_done++;
if ( a->acl_op ) {
slap_restrictop_t restrictop = slap_ops2restrict[slap_req2op( op->o_tag )];
if ( !(a->acl_op & restrictop) )
continue;
if ( restrictop == SLAP_RESTRICT_OP_EXTENDED && !BER_BVISNULL( &a->acl_oid ) &&
ber_bvcmp( &a->acl_oid, &op->oq_extended.rs_reqoid ) != 0 )
continue;
}
if ( a->acl_control && op->o_ctrlflag[a->acl_control] <= SLAP_CONTROL_IGNORED )
continue;
if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n",

View file

@ -631,6 +631,51 @@ parse_acl(
}
}
} else if ( strcasecmp( left, "control" ) == 0 ) {
char *oid = oidm_find( right );
if ( !oid ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"bad control OID \"%s\" in to clause", right );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
goto fail;
}
if ( slap_find_control_id( oid, &a->acl_control ) ) {
if ( oid != right ) {
ch_free( oid );
}
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"unknown control \"%s\" in to clause", right );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
goto fail;
}
ber_str2bv( right, 0, 1, &a->acl_controlval );
if ( oid != right ) {
ch_free( oid );
}
} else if ( strcasecmp( left, "op" ) == 0 ) {
int i = verb_to_mask( right, slap_restrictable_ops );
a->acl_op = slap_restrictable_ops[i].mask;
if ( !a->acl_op ) {
char *oid = oidm_find( right );
if ( oid ) {
ber_str2bv( oid, 0, oid == right, &a->acl_oid );
a->acl_op = SLAP_RESTRICT_OP_EXTENDED;
}
}
/* Also filter out "extended=..." because those should be
* specified by OID directly */
a->acl_op &= SLAP_RESTRICT_OP_MASK;
if ( !a->acl_op ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"unsupported operation type \"%s\" in to clause", right );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
goto fail;
}
ber_str2bv( right, 0, 1, &a->acl_opval );
} else {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"expecting <what> got \"%s\"",
@ -2166,9 +2211,11 @@ acl_usage(void)
"[ by <who> [ <access> ] [ <control> ] ]+ \n";
char *what =
"<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
"\t[op=<opspec>] [control=<oid>]\n"
"<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
"<attrlist> ::= <attr> [ , <attrlist> ]\n"
"<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
"<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n"
"<opspec> ::= <LDAPOperation> | <oid>\n";
char *who =
"<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
@ -2364,6 +2411,13 @@ acl_free( AccessControl *a )
ber_memfree( a->acl_attrval.bv_val );
}
}
if ( !BER_BVISNULL( &a->acl_controlval ) ) {
free( a->acl_controlval.bv_val );
}
if ( !BER_BVISNULL( &a->acl_opval ) ) {
free( a->acl_opval.bv_val );
free( a->acl_oid.bv_val );
}
for ( ; a->acl_access; a->acl_access = n ) {
n = a->acl_access->a_next;
access_free( a->acl_access );
@ -2777,6 +2831,20 @@ acl_unparse( AccessControl *a, struct berval *bv )
ptr = acl_safe_strcopy( ptr, "\"\n" );
}
if ( !BER_BVISNULL( &a->acl_opval ) ) {
to++;
ptr = acl_safe_strcopy( ptr, " op=" );
ptr = acl_safe_strbvcopy( ptr, &a->acl_opval );
ptr = acl_safe_strcopy( ptr, "\n" );
}
if ( !BER_BVISNULL( &a->acl_controlval ) ) {
to++;
ptr = acl_safe_strcopy( ptr, " control=" );
ptr = acl_safe_strbvcopy( ptr, &a->acl_controlval );
ptr = acl_safe_strcopy( ptr, "\n" );
}
if ( !to ) {
ptr = acl_safe_strcopy( ptr, " *\n" );
}

View file

@ -3559,39 +3559,20 @@ static int
config_restrict(ConfigArgs *c) {
slap_mask_t restrictops = 0;
int i;
slap_verbmasks restrictable_ops[] = {
{ BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
{ BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
{ BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
{ BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
{ BER_BVC("modrdn"), 0 },
{ BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
{ BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
{ BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
{ BER_BVC("read"), SLAP_RESTRICT_OP_READS },
{ BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
{ BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
{ BER_BVC("extended=" LDAP_EXOP_START_TLS ), SLAP_RESTRICT_EXOP_START_TLS },
{ BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD ), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
{ BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I ), SLAP_RESTRICT_EXOP_WHOAMI },
{ BER_BVC("extended=" LDAP_EXOP_X_CANCEL ), SLAP_RESTRICT_EXOP_CANCEL },
{ BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
{ BER_BVNULL, 0 }
};
if (c->op == SLAP_CONFIG_EMIT) {
return mask_to_verbs( restrictable_ops, c->be->be_restrictops,
return mask_to_verbs( slap_restrictable_ops, c->be->be_restrictops,
&c->rvalue_vals );
} else if ( c->op == LDAP_MOD_DELETE ) {
if ( !c->line ) {
c->be->be_restrictops = 0;
} else {
i = verb_to_mask( c->line, restrictable_ops );
c->be->be_restrictops &= ~restrictable_ops[i].mask;
i = verb_to_mask( c->line, slap_restrictable_ops );
c->be->be_restrictops &= ~slap_restrictable_ops[i].mask;
}
return 0;
}
i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
i = verbs_to_mask( c->argc, c->argv, slap_restrictable_ops, &restrictops );
if ( i ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown operation", c->argv[0] );
Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",

View file

@ -1156,6 +1156,56 @@ static slap_verbmasks versionkey[] = {
{ BER_BVNULL, 0 }
};
static slap_verbmasks slap_ops_[] = {
{ BER_BVC("bind"), SLAP_OP_BIND },
{ BER_BVC("unbind"), SLAP_OP_UNBIND },
{ BER_BVC("search"), SLAP_OP_SEARCH },
{ BER_BVC("compare"), SLAP_OP_COMPARE },
{ BER_BVC("modify"), SLAP_OP_MODIFY },
{ BER_BVC("rename"), SLAP_OP_RENAME },
{ BER_BVC("modrdn"), SLAP_OP_MODRDN },
{ BER_BVC("add"), SLAP_OP_ADD },
{ BER_BVC("delete"), SLAP_OP_DELETE },
{ BER_BVC("abandon"), SLAP_OP_ABANDON },
{ BER_BVC("extended"), SLAP_OP_EXTENDED },
{ BER_BVNULL, SLAP_OP_LAST }
};
slap_verbmasks *slap_ops = slap_ops_;
static slap_verbmasks slap_restrictable_ops_[] = {
{ BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
{ BER_BVC("read"), SLAP_RESTRICT_OP_READS },
{ BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
{ BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
{ BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
{ BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
{ BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
{ BER_BVC("modrdn"), SLAP_RESTRICT_OP_MODRDN },
{ BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
{ BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
{ BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
{ BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
{ BER_BVC("extended=" LDAP_EXOP_START_TLS ), SLAP_RESTRICT_EXOP_START_TLS },
{ BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD ), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
{ BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I ), SLAP_RESTRICT_EXOP_WHOAMI },
{ BER_BVC("extended=" LDAP_EXOP_X_CANCEL ), SLAP_RESTRICT_EXOP_CANCEL },
{ BER_BVNULL, 0 }
};
slap_verbmasks *slap_restrictable_ops = slap_restrictable_ops_;
static slap_restrictop_t slap_ops2restrict_[] = {
[SLAP_OP_ADD] = SLAP_RESTRICT_OP_ADD,
[SLAP_OP_BIND] = SLAP_RESTRICT_OP_BIND,
[SLAP_OP_COMPARE] = SLAP_RESTRICT_OP_COMPARE,
[SLAP_OP_DELETE] = SLAP_RESTRICT_OP_DELETE,
[SLAP_OP_EXTENDED] = SLAP_RESTRICT_OP_EXTENDED,
[SLAP_OP_MODIFY] = SLAP_RESTRICT_OP_MODIFY,
[SLAP_OP_MODRDN] = SLAP_RESTRICT_OP_MODRDN,
[SLAP_OP_SEARCH] = SLAP_RESTRICT_OP_SEARCH,
[SLAP_OP_LAST] = 0
};
slap_restrictop_t *slap_ops2restrict = slap_ops2restrict_;
int
slap_keepalive_parse(
struct berval *val,

View file

@ -745,6 +745,10 @@ LDAP_SLAPD_F (int) slap_bv_x_ordered_unparse LDAP_P(( BerVarray in, BerVarray *o
LDAP_SLAPD_F (int) slap_keepalive_parse( struct berval *val, void *bc,
slap_cf_aux_table *tab0, const char *tabmsg, int unparse );
LDAP_SLAPD_V (slap_verbmasks *) slap_ops;
LDAP_SLAPD_V (slap_verbmasks *) slap_restrictable_ops;
LDAP_SLAPD_V (slap_restrictop_t *) slap_ops2restrict;
#ifdef LDAP_SLAPI
LDAP_SLAPD_V (int) slapi_plugins_used;
#endif

View file

@ -56,6 +56,59 @@
# Select informational schema items:
# RFC 2377 (uidObject)
#
# Standards track control object identifiers
#
objectIdentifier manageDSAiT 2.16.840.1.113730.3.4.2
objectIdentifier proxyAuthz 2.16.840.1.113730.3.4.18
objectIdentifier subentries 1.3.6.1.4.1.4203.1.10.1
objectIdentifier valuesReturnFilter 1.2.826.0.1.3344810.2.3
objectIdentifier assert 1.3.6.1.1.12
objectIdentifier preread 1.3.6.1.1.13.1
objectIdentifier postread 1.3.6.1.1.13.2
objectIdentifier sort 1.2.840.113556.1.4.473
objectIdentifier pagedResults 1.2.840.113556.1.4.319
objectIdentifier authzId 2.16.840.1.113730.3.4.16
objectIdentifier sync 1.3.6.1.4.1.4203.1.9.1.1
objectIdentifier dontUseCopy 1.3.6.1.1.22
objectIdentifier ppolicy 1.3.6.1.4.1.42.2.27.8.5.1
objectIdentifier noop 1.3.6.1.4.1.4203.666.5.2
objectIdentifier nosubordinates 1.3.6.1.4.1.4203.666.5.11
objectIdentifier relax 1.3.6.1.4.1.4203.666.5.12
objectIdentifier slurp 1.3.6.1.4.1.4203.666.5.13
objectIdentifier valsort 1.3.6.1.4.1.4203.666.5.14
objectIdentifier x-deref 1.3.6.1.4.1.4203.666.5.16
objectIdentifier x-whatfailed 1.3.6.1.4.1.4203.666.5.17
objectIdentifier x-chainingBehaviour 1.3.6.1.4.1.4203.666.11.3
objectIdentifier x-lazyCommit 1.2.840.113556.1.4.619
objectIdentifier x-domainScope 1.2.840.113556.1.4.1339
objectIdentifier x-permissiveModify 1.2.840.113556.1.4.1413
objectIdentifier x-searchOptions 1.2.840.113556.1.4.1340
objectIdentifier x-treeDelete 1.2.840.113556.1.4.805
objectIdentifier x-serverNotification 1.2.840.113556.1.4.528
objectIdentifier x-extendedDN 1.2.840.113556.1.4.528
objectIdentifier x-showDeleted 1.2.840.113556.1.4.417
objectIdentifier x-dirSync 1.2.840.113556.1.4.841
objectIdentifier sessionTracking 1.3.6.1.4.1.21008.108.63.1
objectIdentifier dupent 2.16.840.1.113719.1.27.101.1
objectIdentifier persist 2.16.840.1.113730.3.4.3
objectIdentifier vlv 2.16.840.1.113730.3.4.9
objectIdentifier x-accountUsability 1.3.6.1.4.1.42.2.27.9.5.8
objectIdentifier x-passwordExpired 2.16.840.1.113730.3.4.4
objectIdentifier x-passwordExpiring 2.16.840.1.113730.3.4.5
objectIdentifier x-returnContinuationReference 1.3.6.1.4.1.4203.666.11.6.3
objectIdentifier modifyPassword 1.3.6.1.4.1.4203.1.11.1
objectIdentifier cancel 1.3.6.1.1.8
objectIdentifier refresh 1.3.6.1.4.1.1466.101.119.1
objectIdentifier verifyCredentials 1.3.6.1.4.1.4203.666.6.5
objectIdentifier whoAmI 1.3.6.1.4.1.4203.1.11.3
objectIdentifier turn 1.3.6.1.1.19
objectIdentifier x-chainedRequest 1.3.6.1.4.1.4203.666.11.6.1
#
# Standard attribute types from RFC 2256
#

View file

@ -1311,6 +1311,64 @@ typedef enum slap_style_e {
ACL_STYLE_NONE
} slap_style_t;
/*
* Operation indices
*/
typedef enum {
SLAP_OP_BIND = 0,
SLAP_OP_UNBIND,
SLAP_OP_SEARCH,
SLAP_OP_COMPARE,
SLAP_OP_MODIFY,
SLAP_OP_MODRDN,
SLAP_OP_RENAME = SLAP_OP_MODRDN,
SLAP_OP_ADD,
SLAP_OP_DELETE,
SLAP_OP_ABANDON,
SLAP_OP_EXTENDED,
SLAP_OP_LAST
} slap_op_t;
typedef enum {
SLAP_RESTRICT_OP_NONE = 0,
SLAP_RESTRICT_OP_ADD = 0x0001U,
SLAP_RESTRICT_OP_BIND = 0x0002U,
SLAP_RESTRICT_OP_COMPARE = 0x0004U,
SLAP_RESTRICT_OP_DELETE = 0x0008U,
SLAP_RESTRICT_OP_EXTENDED = 0x0010U,
SLAP_RESTRICT_OP_MODIFY = 0x0020U,
SLAP_RESTRICT_OP_RENAME = 0x0040U,
SLAP_RESTRICT_OP_MODRDN = SLAP_RESTRICT_OP_RENAME,
SLAP_RESTRICT_OP_SEARCH = 0x0080U,
SLAP_RESTRICT_OP_READS = (
SLAP_RESTRICT_OP_COMPARE |
SLAP_RESTRICT_OP_SEARCH ),
SLAP_RESTRICT_OP_WRITES = (
SLAP_RESTRICT_OP_ADD |
SLAP_RESTRICT_OP_DELETE |
SLAP_RESTRICT_OP_MODIFY |
SLAP_RESTRICT_OP_RENAME ),
SLAP_RESTRICT_OP_ALL = (
SLAP_RESTRICT_OP_READS |
SLAP_RESTRICT_OP_WRITES |
SLAP_RESTRICT_OP_BIND |
SLAP_RESTRICT_OP_EXTENDED ),
SLAP_RESTRICT_OP_MASK = 0x00FFU,
SLAP_RESTRICT_EXOP_START_TLS = 0x0100U,
SLAP_RESTRICT_EXOP_MODIFY_PASSWD = 0x0200U,
SLAP_RESTRICT_EXOP_WHOAMI = 0x0400U,
SLAP_RESTRICT_EXOP_CANCEL = 0x0800U,
SLAP_RESTRICT_EXOP_MASK = 0xFF00U,
SLAP_RESTRICT_READONLY = 0x80000000U,
} slap_restrictop_t;
typedef struct {
slap_op_t op;
slap_restrictop_t restictop;
} slap_ops2restrict_t;
typedef struct AuthorizationInformation {
ber_tag_t sai_method; /* LDAP_AUTH_* from <ldap.h> */
struct berval sai_mech; /* SASL Mechanism */
@ -1553,6 +1611,13 @@ typedef struct AccessControl {
slap_style_t acl_attrval_style;
regex_t acl_attrval_re;
struct berval acl_attrval;
slap_restrictop_t acl_op;
struct berval acl_oid;
int acl_control;
/* Preserved op= and control= parts as configured */
struct berval acl_opval;
struct berval acl_controlval;
/* "by" part: list of who has what access to the entries */
Access *acl_access;
@ -1934,38 +1999,7 @@ struct BackendDB {
#define SLAP_SYNC_SUBENTRY(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SYNC_SUBENTRY)
#define SLAP_LASTBIND_ASSERT(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_LASTBIND_ASSERT)
slap_mask_t be_restrictops; /* restriction operations */
#define SLAP_RESTRICT_OP_ADD 0x0001U
#define SLAP_RESTRICT_OP_BIND 0x0002U
#define SLAP_RESTRICT_OP_COMPARE 0x0004U
#define SLAP_RESTRICT_OP_DELETE 0x0008U
#define SLAP_RESTRICT_OP_EXTENDED 0x0010U
#define SLAP_RESTRICT_OP_MODIFY 0x0020U
#define SLAP_RESTRICT_OP_RENAME 0x0040U
#define SLAP_RESTRICT_OP_SEARCH 0x0080U
#define SLAP_RESTRICT_OP_MASK 0x00FFU
#define SLAP_RESTRICT_READONLY 0x80000000U
#define SLAP_RESTRICT_EXOP_START_TLS 0x0100U
#define SLAP_RESTRICT_EXOP_MODIFY_PASSWD 0x0200U
#define SLAP_RESTRICT_EXOP_WHOAMI 0x0400U
#define SLAP_RESTRICT_EXOP_CANCEL 0x0800U
#define SLAP_RESTRICT_EXOP_MASK 0xFF00U
#define SLAP_RESTRICT_OP_READS \
( SLAP_RESTRICT_OP_COMPARE \
| SLAP_RESTRICT_OP_SEARCH )
#define SLAP_RESTRICT_OP_WRITES \
( SLAP_RESTRICT_OP_ADD \
| SLAP_RESTRICT_OP_DELETE \
| SLAP_RESTRICT_OP_MODIFY \
| SLAP_RESTRICT_OP_RENAME )
#define SLAP_RESTRICT_OP_ALL \
( SLAP_RESTRICT_OP_READS \
| SLAP_RESTRICT_OP_WRITES \
| SLAP_RESTRICT_OP_BIND \
| SLAP_RESTRICT_OP_EXTENDED )
slap_restrictop_t be_restrictops; /* restriction operations */
#define SLAP_ALLOW_BIND_V2 0x0001U /* LDAPv2 bind */
#define SLAP_ALLOW_BIND_ANON_CRED 0x0002U /* cred should be empty */
@ -2561,23 +2595,6 @@ struct slap_control_ids {
#endif
};
/*
* Operation indices
*/
typedef enum {
SLAP_OP_BIND = 0,
SLAP_OP_UNBIND,
SLAP_OP_SEARCH,
SLAP_OP_COMPARE,
SLAP_OP_MODIFY,
SLAP_OP_MODRDN,
SLAP_OP_ADD,
SLAP_OP_DELETE,
SLAP_OP_ABANDON,
SLAP_OP_EXTENDED,
SLAP_OP_LAST
} slap_op_t;
typedef struct slap_counters_t {
struct slap_counters_t *sc_next;
ldap_pvt_thread_mutex_t sc_mutex;

View file

@ -131,6 +131,12 @@ access to dn.exact="cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com"
access to filter="(name=X*Y*Z)"
by * continue
access to dn.subtree="ou=Add & Delete,dc=example,dc=com" control=manageDSAiT
by * read
access to dn.subtree="ou=Add & Delete,dc=example,dc=com" op=search
by * write
access to dn.subtree="ou=Add & Delete,dc=example,dc=com"
by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" add
by dn.exact="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" delete

View file

@ -613,6 +613,27 @@ case $RC in
;;
esac
$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen -M >> \
$TESTOUT 2>&1 << EOMODS15a
dn: cn=Added by Bjorn (will be deleted),ou=Add & Delete,dc=example,dc=com
changetype: delete
EOMODS15a
RC=$?
case $RC in
50)
;;
0)
echo "ldapmodify should have failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit 1
;;
*)
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
;;
esac
$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
$TESTOUT 2>&1 << EOMODS15
dn: cn=Added by Bjorn (will be deleted),ou=Add & Delete,dc=example,dc=com