diff --git a/CHANGES b/CHANGES
index c2e110eedd7d4f7b887168a6aa7f0344face4a28..3d3c54424e23b5d6f72a65688c5b229ad402c359 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,7 @@ OpenLDAP 2.4.12 Engineering
 	Fixed slapd custom attribute inheritance (ITS#5642)
 	Fixed slapd dynacl mask handling (ITS#5637)
 	Fixed slapd firstComponentMatch normalization (ITS#5634)
+	Added slapd caseIgnoreListMatch (ITS#5608)
 	Fixed slapd overlay control registration (ITS#5649)
 	Fixed slapd socket closing on Windows (ITS#5606)
 	Fixed slapd syncrepl error logging (ITS#5618)
diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c
index fd386a74f2e2d2b59df75b07bc113a5566bd6171..2d8e75c34c4964604a884074d11c674b565bc3f6 100644
--- a/servers/slapd/schema_init.c
+++ b/servers/slapd/schema_init.c
@@ -1987,6 +1987,119 @@ telephoneNumberNormalize(
 	return LDAP_SUCCESS;
 }
 
+static int
+postalAddressValidate(
+	Syntax *syntax,
+	struct berval *in )
+{
+	struct berval bv = *in;
+	int c;
+
+	for ( c = 0; c < in->bv_len; c++ ) {
+		if ( in->bv_val[c] == '\\' ) {
+			c++;
+			if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0
+				&& strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 )
+			{
+				return LDAP_INVALID_SYNTAX;
+			}
+			continue;
+		}
+
+		if ( in->bv_val[c] == '$' ) {
+			bv.bv_len = &in->bv_val[c] - bv.bv_val;
+			if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) {
+				return LDAP_INVALID_SYNTAX;
+			}
+			bv.bv_val = &in->bv_val[c] + 1;
+		}
+	}
+
+	bv.bv_len = &in->bv_val[c] - bv.bv_val;
+	return UTF8StringValidate( NULL, &bv );
+}
+
+static int
+postalAddressNormalize(
+	slap_mask_t usage,
+	Syntax *syntax,
+	MatchingRule *mr,
+	struct berval *val,
+	struct berval *normalized,
+	void *ctx )
+{
+	BerVarray lines = NULL, nlines = NULL;
+	int l, c;
+	int rc = LDAP_SUCCESS;
+	MatchingRule *xmr = NULL;
+	char *p;
+
+	if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) {
+		xmr = slap_schema.si_mr_caseIgnoreMatch;
+
+	} else {
+		xmr = slap_schema.si_mr_caseExactMatch;
+	}
+
+	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
+		if ( val->bv_val[c] == '$' ) {
+			l++;
+		}
+	}
+
+	lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx );
+	nlines = &lines[l + 2];
+
+	lines[0].bv_val = val->bv_val;
+	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
+		if ( val->bv_val[c] == '$' ) {
+			lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
+			l++;
+			lines[l].bv_val = &val->bv_val[c + 1];
+		}
+	}
+	lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
+
+	normalized->bv_len = l;
+
+	for ( l = 0; !BER_BVISNULL( &lines[l] ); l++ ) {
+		/* NOTE: we directly normalize each line,
+		 * without unescaping the values, since the special
+		 * values '\24' ('$') and '\5C' ('\') are not affected
+		 * by normalization */
+		rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx );
+		if ( rc != LDAP_SUCCESS ) {
+			rc = LDAP_INVALID_SYNTAX;
+			goto done;
+		}
+
+		normalized->bv_len += nlines[l].bv_len;
+	}
+
+	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
+
+	p = normalized->bv_val;
+	for ( l = 0; !BER_BVISNULL( &nlines[l] ); l++ ) {
+		p = lutil_strncopy( p, nlines[l].bv_val, nlines[l].bv_len );
+
+		*p++ = '$';
+	}
+	*--p = '\0';
+
+	assert( p - normalized->bv_val == normalized->bv_len );
+
+done:;
+	if ( nlines != NULL ) {
+		for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) {
+			slap_sl_free( nlines[l].bv_val, ctx );
+		}
+
+		slap_sl_free( lines, ctx );
+	}
+
+	return rc;
+}
+
 int
 numericoidValidate(
 	Syntax *syntax,
@@ -4688,7 +4801,7 @@ static slap_syntax_defs_rec syntax_defs[] = {
 	{"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
 		0, NULL, blobValidate, NULL},
 	{"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
-		0, NULL, UTF8StringValidate, NULL},
+		0, NULL, postalAddressValidate, NULL},
 	{"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )",
 		0, NULL, NULL, NULL},
 	{"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )",
@@ -4993,7 +5106,9 @@ static slap_mrule_defs_rec mrule_defs[] = {
 	{"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )",
 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
-		NULL, NULL, NULL, NULL, NULL, NULL },
+		NULL, postalAddressNormalize, octetStringMatch,
+		octetStringIndexer, octetStringFilter,
+		NULL },
 
 	{"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
diff --git a/servers/slapd/schema_prep.c b/servers/slapd/schema_prep.c
index 42f9a5e94334f93f43fcb644e54276678982c7b5..28bcc96c1debd61e3f804f43783a754e1263ee83 100644
--- a/servers/slapd/schema_prep.c
+++ b/servers/slapd/schema_prep.c
@@ -1072,6 +1072,10 @@ static struct slap_schema_mr_map {
 	{ "objectIdentifierFirstComponentMatch",
 		offsetof(struct slap_internal_schema,
 			si_mr_objectIdentifierFirstComponentMatch) },
+	{ "caseIgnoreMatch",
+		offsetof(struct slap_internal_schema, si_mr_caseIgnoreMatch) },
+	{ "caseIgnoreListMatch",
+		offsetof(struct slap_internal_schema, si_mr_caseIgnoreListMatch) },
 	{ NULL, 0 }
 };
 
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index 431a7743b76fe3fc65a31c875974eefab43fd05c..95c4e898ac109f7a92885d54341f2b928e95c37f 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -969,6 +969,8 @@ struct slap_internal_schema {
 	MatchingRule	*si_mr_integerMatch;
 	MatchingRule    *si_mr_integerFirstComponentMatch;
 	MatchingRule    *si_mr_objectIdentifierFirstComponentMatch;
+	MatchingRule    *si_mr_caseIgnoreMatch;
+	MatchingRule    *si_mr_caseIgnoreListMatch;
 
 	/* Syntaxes */
 	Syntax		*si_syn_directoryString;