ITS#9640 Introduce the increment privilege

This commit is contained in:
Ondřej Kuzník 2025-12-10 12:40:11 +00:00 committed by Quanah Gibson-Mount
parent 8423637333
commit b8df689969
8 changed files with 199 additions and 15 deletions

View file

@ -760,8 +760,8 @@ field will have.
Its component are defined as
.LP
.nf
<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage
<priv> ::= {=|+|\-}{0|d|x|c|s|r|{w|a|z}|m}+
<level> ::= none|disclose|auth|compare|search|read|{write|add|delete|increment}|manage
<priv> ::= {=|+|\-}{0|d|x|c|s|r|{w|a|z|i}|m}+
.fi
.LP
The modifier
@ -805,11 +805,12 @@ attribute that is defined as not user modifiable.
The
.BR write
access is actually the combination of
.BR add
and
.BR add ,
.BR delete ,
which respectively restrict the write privilege to add or delete
the specified
and
.BR increment ,
which respectively restrict the write privilege to add, delete or
increment the specified
.BR <what> .
.LP
@ -852,6 +853,8 @@ for write,
for add,
.B z
for delete,
.B i
for increment,
.B r
for read,
.B s
@ -866,7 +869,7 @@ More than one of the above privileges can be added in one statement.
.B 0
indicates no privileges and is used only by itself (e.g., +0).
Note that
.B +az
.B +azi
is equivalent to
.BR +w .
.LP
@ -1013,6 +1016,8 @@ In detail,
is required to add new values,
.B delete (=z)
is required to delete existing values,
.B increment (=i)
is required to increment values,
and both
.B delete
and

View file

@ -1967,8 +1967,21 @@ acl_check_modlist(
}
switch ( mlist->sml_op ) {
case LDAP_MOD_REPLACE:
case LDAP_MOD_INCREMENT:
assert( mlist->sml_values != NULL );
assert( BER_BVISNULL( &mlist->sml_values[1] ) );
if ( ! access_allowed( op, e,
mlist->sml_desc, &mlist->sml_values[0],
( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WINCR,
&state ) )
{
ret = 0;
goto done;
}
break;
case LDAP_MOD_REPLACE:
/*
* We must check both permission to delete the whole
* attribute and permission to add the specific attributes.

View file

@ -1961,6 +1961,9 @@ accessmask2str( slap_mask_t mask, char *buf, int debug )
} else if ( ACL_LVL_IS_WDEL(mask) ) {
ptr = lutil_strcopy( ptr, "delete" );
} else if ( ACL_LVL_IS_WINCR(mask) ) {
ptr = lutil_strcopy( ptr, "increment" );
} else if ( ACL_LVL_IS_MANAGE(mask) ) {
ptr = lutil_strcopy( ptr, "manage" );
@ -2001,6 +2004,10 @@ accessmask2str( slap_mask_t mask, char *buf, int debug )
} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
none = 0;
*ptr++ = 'z';
} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WINCR) ) {
none = 0;
*ptr++ = 'i';
}
if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
@ -2081,6 +2088,9 @@ str2accessmask( const char *str )
} else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
} else if( TOLOWER((unsigned char) str[i]) == 'i' ) {
ACL_PRIV_SET(mask, ACL_PRIV_WINCR);
} else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
ACL_PRIV_SET(mask, ACL_PRIV_READ);
@ -2132,6 +2142,9 @@ str2accessmask( const char *str )
} else if ( strcasecmp( str, "delete" ) == 0 ) {
ACL_LVL_ASSIGN_WDEL(mask);
} else if ( strcasecmp( str, "increment" ) == 0 ) {
ACL_LVL_ASSIGN_WINCR(mask);
} else if ( strcasecmp( str, "write" ) == 0 ) {
ACL_LVL_ASSIGN_WRITE(mask);
@ -2177,8 +2190,8 @@ acl_usage(void)
"<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
"<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
"<access> ::= [[real]self]{<level>|<priv>}\n"
"<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
"<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
"<level> ::= none|disclose|auth|compare|search|read|{write|add|delete|increment}|manage\n"
"<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z|i}|m}+\n"
"<control> ::= [ stop | continue | break ]\n"
#ifdef SLAP_DYNACL
#ifdef SLAPD_ACI_ENABLED
@ -2404,6 +2417,9 @@ access2str( slap_access_t access )
} else if ( access == ACL_WDEL ) {
return "delete";
} else if ( access == ACL_WDEL ) {
return "increment";
} else if ( access == ACL_MANAGE ) {
return "manage";
@ -2442,6 +2458,9 @@ str2access( const char *str )
} else if ( strcasecmp( str, "delete" ) == 0 ) {
return ACL_WDEL;
} else if ( strcasecmp( str, "increment" ) == 0 ) {
return ACL_WDEL;
} else if ( strcasecmp( str, "manage" ) == 0 ) {
return ACL_MANAGE;
}

View file

@ -1069,7 +1069,8 @@ constraint_update( Operation *op, SlapReply *rs )
if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE) &&
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_INCREMENT))
continue;
/* we only care about ADD and REPLACE modifications */
/* and DELETE are used to track attribute count */

View file

@ -1280,8 +1280,9 @@ typedef enum slap_access_t {
/* write granularity */
ACL_WADD = ACL_WRITE_|ACL_QUALIFIER1,
ACL_WDEL = ACL_WRITE_|ACL_QUALIFIER2,
ACL_WINCR = ACL_WRITE_|ACL_QUALIFIER3,
ACL_WRITE = ACL_WADD|ACL_WDEL
ACL_WRITE = ACL_WADD|ACL_WDEL|ACL_WINCR
} slap_access_t;
typedef enum slap_control_e {
@ -1390,7 +1391,8 @@ typedef struct Access {
#define ACL_PRIV_READ ACL_ACCESS2PRIV( ACL_READ )
#define ACL_PRIV_WADD ACL_ACCESS2PRIV( ACL_WADD )
#define ACL_PRIV_WDEL ACL_ACCESS2PRIV( ACL_WDEL )
#define ACL_PRIV_WRITE ( ACL_PRIV_WADD | ACL_PRIV_WDEL )
#define ACL_PRIV_WINCR ACL_ACCESS2PRIV( ACL_WINCR )
#define ACL_PRIV_WRITE ( ACL_PRIV_WADD | ACL_PRIV_WDEL | ACL_PRIV_WINCR )
#define ACL_PRIV_MANAGE ACL_ACCESS2PRIV( ACL_MANAGE )
/* NOTE: always use the highest level; current: 0x00ffUL */
@ -1428,6 +1430,7 @@ typedef struct Access {
#define ACL_LVL_READ (ACL_PRIV_READ|ACL_LVL_SEARCH)
#define ACL_LVL_WADD (ACL_PRIV_WADD|ACL_LVL_READ)
#define ACL_LVL_WDEL (ACL_PRIV_WDEL|ACL_LVL_READ)
#define ACL_LVL_WINCR (ACL_PRIV_WINCR|ACL_LVL_READ)
#define ACL_LVL_WRITE (ACL_PRIV_WRITE|ACL_LVL_READ)
#define ACL_LVL_MANAGE (ACL_PRIV_MANAGE|ACL_LVL_WRITE)
@ -1440,6 +1443,7 @@ typedef struct Access {
#define ACL_LVL_IS_READ(m) ACL_LVL((m),ACL_LVL_READ)
#define ACL_LVL_IS_WADD(m) ACL_LVL((m),ACL_LVL_WADD)
#define ACL_LVL_IS_WDEL(m) ACL_LVL((m),ACL_LVL_WDEL)
#define ACL_LVL_IS_WINCR(m) ACL_LVL((m),ACL_LVL_WINCR)
#define ACL_LVL_IS_WRITE(m) ACL_LVL((m),ACL_LVL_WRITE)
#define ACL_LVL_IS_MANAGE(m) ACL_LVL((m),ACL_LVL_MANAGE)
@ -1451,6 +1455,7 @@ typedef struct Access {
#define ACL_LVL_ASSIGN_READ(m) ACL_PRIV_ASSIGN((m),ACL_LVL_READ)
#define ACL_LVL_ASSIGN_WADD(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WADD)
#define ACL_LVL_ASSIGN_WDEL(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WDEL)
#define ACL_LVL_ASSIGN_WINCR(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WINCR)
#define ACL_LVL_ASSIGN_WRITE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WRITE)
#define ACL_LVL_ASSIGN_MANAGE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_MANAGE)

View file

@ -362,6 +362,6 @@ dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
objectClass: extensibleObject
ou: People
uidNumber: 0
gidNumber: 0
uidNumber: 56
gidNumber: 50

View file

@ -137,6 +137,18 @@ access to dn.subtree="ou=Add & Delete,dc=example,dc=com"
by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" write
by * read
access to dn="ou=People,dc=example,dc=com"
by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" write continue
by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" -i
by * break
access to dn="ou=People,dc=example,dc=com" attrs=uidNumber val.regex="^5*$"
by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" increment
by * break
access to dn="ou=People,dc=example,dc=com" attrs=uidNumber val="1"
by dn.exact="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" increment
# fall into global ACLs
database monitor

View file

@ -635,6 +635,135 @@ case $RC in
;;
esac
$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
$TESTOUT 2>&1 << EOMODS16
dn: ou=People,dc=example,dc=com
changetype: modify
increment: uidNumber
uidNumber: 50
-
EOMODS16
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 "$JAJDN" -H $URI1 -w jaj >> \
$TESTOUT 2>&1 << EOMODS17
dn: ou=People,dc=example,dc=com
changetype: modify
replace: gidNumber
gidNumber: 50
-
EOMODS17
RC=$?
case $RC in
0)
;;
*)
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
;;
esac
$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
$TESTOUT 2>&1 << EOMODS18
dn: ou=People,dc=example,dc=com
changetype: modify
increment: uidNumber
uidNumber: 55
-
EOMODS18
RC=$?
case $RC in
0)
;;
*)
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
;;
esac
$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
$TESTOUT 2>&1 << EOMODS19
dn: ou=People,dc=example,dc=com
changetype: modify
increment: uidNumber
uidNumber: -1
-
EOMODS19
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 << EOMODS20
dn: ou=People,dc=example,dc=com
changetype: modify
increment: uidNumber
uidNumber: 1
-
EOMODS20
RC=$?
case $RC in
0)
;;
*)
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
;;
esac
$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
$TESTOUT 2>&1 << EOMODS21
dn: ou=People,dc=example,dc=com
changetype: modify
replace: uidNumber
uidNumber: 55
-
EOMODS21
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
echo "Using ldapsearch to retrieve all the entries..."
echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \