diff --git a/CHANGES b/CHANGES
index 27343d928a4a1f4dae2c27a7584a6210a9e08427..ec407644d16baa7d4f97e99b433fe0a0490919a7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,7 @@ OpenLDAP 2.4 Change Log
 
 OpenLDAP 2.4.13 Engineering
 	Fixed liblutil hex conversion (ITS#5699)
+	Added slapd dn.this ACL limits (ITS#5734)
 	Fixed slapd nameUIDPretty bitstring parsing (ITS#5750)
 	Fixed slapd overlay/database open with real structure (ITS#5724)
 	Fixed slapd parsing of read entry control (ITS#5741)
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
index 47a4af8f103889033cecbd3be89c4d527cddcaac..4c22b3eef233544da9e3c6efac6bd71a555864d4 100644
--- a/doc/man/man5/slapd-config.5
+++ b/doc/man/man5/slapd-config.5
@@ -1250,23 +1250,33 @@ createTimestamp attributes for entries. It also controls
 the entryCSN and entryUUID attributes, which are needed
 by the syncrepl provider. By default, olcLastMod is TRUE.
 .TP
-.B olcLimits: <who> <limit> [<limit> [...]]
-Specify time and size limits based on who initiated an operation.
+.B olcLimits: <selector> <limit> [<limit> [...]]
+Specify time and size limits based on the operation's initiator or
+base DN.
 The argument
-.B who
+.B <selector>
 can be any of
 .RS
 .RS
 .TP
-anonymous | users | [dn[.<style>]=]<pattern> | group[/oc[/at]]=<pattern>
+anonymous | users | [<dnspec>=]<pattern> | group[/oc[/at]]=<pattern>
 
 .RE
 with
 .RS
 .TP
+<dnspec> ::= dn[.<type>][.<style>]
+.TP
+<type>  ::= self | this
+.TP
 <style> ::= exact | base | onelevel | subtree | children | regex | anonymous
 
 .RE
+DN type
+.B self
+is the default and means the bound user, while
+.B this
+means the base DN of the operation.
 The term
 .B anonymous
 matches all unauthenticated clients.
@@ -1300,7 +1310,7 @@ field is ignored.
 The same behavior is obtained by using the 
 .B anonymous
 form of the
-.B who
+.B <selector>
 clause.
 The term
 .BR group ,
@@ -1414,7 +1424,7 @@ limit is set to
 to preserve the original behavior.
 
 In case of no match, the global limits are used.
-The default values are the same as
+The default values are the same as for
 .B olcSizeLimit
 and
 .BR olcTimeLimit ;
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
index 5cb75edf3f9ec7acc59c1af9fb33bf28bdb86654..d4b47c435453ebd4ee8f68f06484701583d41c70 100644
--- a/doc/man/man5/slapd.conf.5
+++ b/doc/man/man5/slapd.conf.5
@@ -1172,23 +1172,33 @@ createTimestamp attributes for entries. It also controls
 the entryCSN and entryUUID attributes, which are needed
 by the syncrepl provider. By default, lastmod is on.
 .TP
-.B limits <who> <limit> [<limit> [...]]
-Specify time and size limits based on who initiated an operation.
+.B limits <selector> <limit> [<limit> [...]]
+Specify time and size limits based on the operation's initiator or
+base DN.
 The argument
-.B who
+.B <selector>
 can be any of
 .RS
 .RS
 .TP
-anonymous | users | [dn[.<style>]=]<pattern> | group[/oc[/at]]=<pattern>
+anonymous | users | [<dnspec>=]<pattern> | group[/oc[/at]]=<pattern>
 
 .RE
 with
 .RS
 .TP
+<dnspec> ::= dn[.<type>][.<style>]
+.TP
+<type>  ::= self | this
+.TP
 <style> ::= exact | base | onelevel | subtree | children | regex | anonymous
 
 .RE
+DN type
+.B self
+is the default and means the bound user, while
+.B this
+means the base DN of the operation.
 The term
 .B anonymous
 matches all unauthenticated clients.
@@ -1222,7 +1232,7 @@ field is ignored.
 The same behavior is obtained by using the 
 .B anonymous
 form of the
-.B who
+.B <selector>
 clause.
 The term
 .BR group ,
@@ -1336,7 +1346,7 @@ limit is set to
 to preserve the original behavior.
 
 In case of no match, the global limits are used.
-The default values are the same of
+The default values are the same as for
 .B sizelimit
 and
 .BR timelimit ;
diff --git a/servers/slapd/limits.c b/servers/slapd/limits.c
index 17e131e1c8c73eb4ffb2205ecf426071d4dc0f7f..a70c0881fb6a377fe1ece81fe621eee37b48feed 100644
--- a/servers/slapd/limits.c
+++ b/servers/slapd/limits.c
@@ -27,6 +27,10 @@
 /* define to get an error if requesting limit higher than hard */
 #undef ABOVE_HARD_LIMIT_IS_ERROR
 
+#ifdef LDAP_DEBUG
+static const char *const dn_source[2] = { "DN", "DN.THIS" };
+#endif
+
 static char *
 limits2str( unsigned i )
 {
@@ -63,21 +67,25 @@ limits2str( unsigned i )
 	}
 }
 
-int
+static int
 limits_get( 
 	Operation		*op,
-	struct berval		*ndn, 
 	struct slap_limits_set 	**limit
 )
 {
 	struct slap_limits **lm;
+	struct berval		*ndns[2];
 
 	assert( op != NULL );
 	assert( limit != NULL );
 
-	Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s dn=\"%s\"\n",
+	ndns[0] = &op->o_ndn;
+	ndns[1] = &op->o_req_ndn;
+
+	Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
 			op->o_log_prefix,
-			BER_BVISNULL( ndn ) ? "[anonymous]" : ndn->bv_val, 0 );
+			BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
+			BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
 	/*
 	 * default values
 	 */
@@ -90,6 +98,8 @@ limits_get(
 	for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
 		unsigned	style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
 		unsigned	type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
+		unsigned	isthis = type == SLAP_LIMITS_TYPE_THIS;
+		struct berval *ndn = ndns[isthis];
 
 		switch ( style ) {
 		case SLAP_LIMITS_EXACT:
@@ -118,8 +128,8 @@ limits_get(
 			
 				if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
 					*limit = &lm[0]->lm_limits;
-					Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=EXACT dn=\"%s\"\n",
-							lm[0]->lm_pat.bv_val, 0, 0 );
+					Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=EXACT dn=\"%s\"\n",
+							dn_source[isthis], lm[0]->lm_pat.bv_val, 0 );
 					return( 0 );
 				}
 			}
@@ -171,8 +181,8 @@ limits_get(
 				}
 
 				*limit = &lm[0]->lm_limits;
-				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
-						limits2str( style ), lm[0]->lm_pat.bv_val, 0 );
+				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n",
+						dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
 				return( 0 );
 			}
 
@@ -187,16 +197,16 @@ limits_get(
 						0, NULL, 0 ) == 0 )
 			{
 				*limit = &lm[0]->lm_limits;
-				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
-						limits2str( style ), lm[0]->lm_pat.bv_val, 0 );
+				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n",
+						dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
 				return( 0 );
 			}
 			break;
 
 		case SLAP_LIMITS_ANONYMOUS:
 			if ( BER_BVISEMPTY( ndn ) ) {
-				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s\n",
-						limits2str( style ), 0, 0 );
+				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
+						dn_source[isthis], limits2str( style ), 0 );
 				*limit = &lm[0]->lm_limits;
 				return( 0 );
 			}
@@ -205,8 +215,8 @@ limits_get(
 		case SLAP_LIMITS_USERS:
 			if ( !BER_BVISEMPTY( ndn ) ) {
 				*limit = &lm[0]->lm_limits;
-				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s\n",
-						limits2str( style ), 0, 0 );
+				Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
+						dn_source[isthis], limits2str( style ), 0 );
 				return( 0 );
 			}
 			break;
@@ -248,6 +258,7 @@ limits_add(
 	case SLAP_LIMITS_ANONYMOUS:
 	case SLAP_LIMITS_USERS:
 	case SLAP_LIMITS_ANY:
+		/* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
 		for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
 			if ( be->be_limits[ i ]->lm_flags == style ) {
 				return( -1 );
@@ -363,10 +374,12 @@ limits_parse(
 	 * 
 	 * "anonymous"
 	 * "users"
-	 * [ "dn" [ "." { "exact" | "base" | "onelevel" | "subtree" | children"
-	 *	| "regex" | "anonymous" } ] "=" ] <dn pattern>
+	 * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
+	 *	"onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
+	 *	"=" ] <dn pattern>
 	 *
 	 * Note:
+	 *	"this" is the baseobject, "self" (the default) is the bound DN
 	 *	"exact" and "base" are the same (exact match);
 	 *	"onelevel" means exactly one rdn below, NOT including pattern
 	 *	"subtree" means any rdn below, including pattern
@@ -396,18 +409,31 @@ limits_parse(
 		
 	} else if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
 		pattern += STRLENOF( "dn" );
+		flags = SLAP_LIMITS_TYPE_SELF;
+		if ( pattern[0] == '.' ) {
+			pattern++;
+			if ( strncasecmp( pattern, "this", STRLENOF( "this" )) == 0 ) {
+				flags = SLAP_LIMITS_TYPE_THIS;
+				pattern += STRLENOF( "this" );
+			} else if ( strncasecmp( pattern, "self", STRLENOF( "self" )) == 0 ) {
+				pattern += STRLENOF( "self" );
+			} else {
+				goto got_dn_dot;
+			}
+		}
 		if ( pattern[0] == '.' ) {
 			pattern++;
+		got_dn_dot:
 			if ( strncasecmp( pattern, "exact", STRLENOF( "exact" )) == 0 ) {
-				flags = SLAP_LIMITS_EXACT;
+				flags |= SLAP_LIMITS_EXACT;
 				pattern += STRLENOF( "exact" );
 
 			} else if ( strncasecmp( pattern, "base", STRLENOF( "base" ) ) == 0 ) {
-				flags = SLAP_LIMITS_BASE;
+				flags |= SLAP_LIMITS_BASE;
 				pattern += STRLENOF( "base" );
 
 			} else if ( strncasecmp( pattern, "one", STRLENOF( "one" ) ) == 0 ) {
-				flags = SLAP_LIMITS_ONE;
+				flags |= SLAP_LIMITS_ONE;
 				pattern += STRLENOF( "one" );
 				if ( strncasecmp( pattern, "level", STRLENOF( "level" ) ) == 0 ) {
 					pattern += STRLENOF( "level" );
@@ -420,7 +446,7 @@ limits_parse(
 				}
 
 			} else if ( strncasecmp( pattern, "sub", STRLENOF( "sub" ) ) == 0 ) {
-				flags = SLAP_LIMITS_SUBTREE;
+				flags |= SLAP_LIMITS_SUBTREE;
 				pattern += STRLENOF( "sub" );
 				if ( strncasecmp( pattern, "tree", STRLENOF( "tree" ) ) == 0 ) {
 					pattern += STRLENOF( "tree" );
@@ -433,18 +459,20 @@ limits_parse(
 				}
 
 			} else if ( strncasecmp( pattern, "children", STRLENOF( "children" ) ) == 0 ) {
-				flags = SLAP_LIMITS_CHILDREN;
+				flags |= SLAP_LIMITS_CHILDREN;
 				pattern += STRLENOF( "children" );
 
 			} else if ( strncasecmp( pattern, "regex", STRLENOF( "regex" ) ) == 0 ) {
-				flags = SLAP_LIMITS_REGEX;
+				flags |= SLAP_LIMITS_REGEX;
 				pattern += STRLENOF( "regex" );
 
 			/* 
 			 * this could be deprecated in favour
 			 * of the pattern = "anonymous" form
 			 */
-			} else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0 ) {
+			} else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0
+					&& flags == SLAP_LIMITS_TYPE_SELF )
+			{
 				flags = SLAP_LIMITS_ANONYMOUS;
 				pattern = NULL;
 			}
@@ -463,8 +491,8 @@ limits_parse(
 			if ( pattern[0] != '=' ) {
 				Debug( LDAP_DEBUG_ANY,
 					"%s : line %d: missing '=' in "
-					"\"dn[.{exact|base|onelevel|subtree"
-					"|children|regex|anonymous}]"
+					"\"dn[.{this|self}][.{exact|base"
+					"|onelevel|subtree|children|regex}]"
 					"=<pattern>\" in "
 					"\"limits <pattern> <limits>\" "
 					"line.\n%s",
@@ -879,13 +907,14 @@ limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
 {
 	struct berval btmp;
 	char *ptr;
-	int lm;
+	int type, lm, dntypelen;
 
 	if ( !bv || !bv->bv_val ) return -1;
 
 	ptr = bv->bv_val;
+	type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
 
-	if (( lim->lm_flags & SLAP_LIMITS_TYPE_MASK ) == SLAP_LIMITS_TYPE_GROUP ) {
+	if ( type == SLAP_LIMITS_TYPE_GROUP ) {
 		if ( WHATSLEFT <= STRLENOF( "group/" "/" "=\"" "\"" )
 				+ lim->lm_group_oc->soc_cname.bv_len
 				+ lim->lm_group_ad->ad_cname.bv_len
@@ -913,9 +942,11 @@ limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
 		case SLAP_LIMITS_SUBTREE:
 		case SLAP_LIMITS_CHILDREN:
 		case SLAP_LIMITS_REGEX:
-			if ( WHATSLEFT <= STRLENOF( "dn." "=" "\"" "\"" )
+			dntypelen = type == SLAP_LIMITS_TYPE_SELF
+				? STRLENOF( "dn." ) : STRLENOF( "dn.this." );
+			if ( WHATSLEFT <= dntypelen + STRLENOF( "=" "\"" "\"" )
 					+ strlen( lmpats[lm] ) + lim->lm_pat.bv_len ) return -1;
-			ptr = lutil_strcopy( ptr, "dn." );
+			ptr = lutil_strncopy( ptr, "dn.this.", dntypelen );
 			ptr = lutil_strcopy( ptr, lmpats[lm] );
 			*ptr++ = '=';
 			*ptr++ = '"';
@@ -1132,7 +1163,7 @@ limits_check( Operation *op, SlapReply *rs )
 
 	/* if not root, get appropriate limits */
 	} else {
-		( void ) limits_get( op, &op->o_ndn, &op->ors_limit );
+		( void ) limits_get( op, &op->ors_limit );
 
 		assert( op->ors_limit != NULL );
 
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 4c2db2960d8ae604a03b1f67213a1dded232a8f8..42aa39fe5ad7d627e206a23150759b0c887140bd 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -1115,9 +1115,6 @@ LDAP_SLAPD_F (int) slap_build_syncUUID_set LDAP_P((
 /*
  * limits.c
  */
-LDAP_SLAPD_F (int) limits_get LDAP_P((
-	Operation *op, struct berval *ndn,
-	struct slap_limits_set **limit ));
 LDAP_SLAPD_F (int) limits_parse LDAP_P((
 	Backend *be, const char *fname, int lineno,
 	int argc, char **argv ));
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index 79f076a7a74ad96d317bfc6a59e7f9a0ac28d6a3..50039b23699fdfcc6f6d01a3194275259ac78744 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -1639,6 +1639,7 @@ struct slap_limits_set {
 
 struct slap_limits {
 	unsigned		lm_flags;	/* type of pattern */
+	/* Values must match lmpats[] in limits.c */
 #define SLAP_LIMITS_UNDEFINED		0x0000U
 #define SLAP_LIMITS_EXACT		0x0001U
 #define SLAP_LIMITS_BASE		SLAP_LIMITS_EXACT
@@ -1651,8 +1652,10 @@ struct slap_limits {
 #define SLAP_LIMITS_ANY			0x0008U
 #define SLAP_LIMITS_MASK		0x000FU
 
-#define SLAP_LIMITS_TYPE_DN		0x0000U
+#define SLAP_LIMITS_TYPE_SELF		0x0000U
+#define SLAP_LIMITS_TYPE_DN		SLAP_LIMITS_TYPE_SELF
 #define SLAP_LIMITS_TYPE_GROUP		0x0010U
+#define SLAP_LIMITS_TYPE_THIS		0x0020U
 #define SLAP_LIMITS_TYPE_MASK		0x00F0U
 
 	regex_t			lm_regex;	/* regex data for REGEX */