From ce847255ff5302ea99d1aab6e86cde31d1e41029 Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Tue, 27 May 2008 21:59:47 +0000
Subject: [PATCH] Add attribute size and count constraints to slapo-constaint

---
 doc/man/man5/slapo-constraint.5     |  12 ++-
 servers/slapd/overlays/constraint.c | 158 +++++++++++++++++++++++-----
 2 files changed, 144 insertions(+), 26 deletions(-)

diff --git a/doc/man/man5/slapo-constraint.5 b/doc/man/man5/slapo-constraint.5
index ad80d05df5..1a60c0e244 100644
--- a/doc/man/man5/slapo-constraint.5
+++ b/doc/man/man5/slapo-constraint.5
@@ -31,7 +31,9 @@ directive.
 Specifies the constraint which should apply to the attribute named as
 the first parameter.
 Two types of constraint are currently supported -
-.B regex
+.B regex ,
+.B size ,
+.B count ,
 and
 .BR uri .
 
@@ -45,6 +47,12 @@ type is an LDAP URI. The URI will be evaluated using an internal search.
 It must not include a hostname, and it must include a list of attributes
 to evaluate.
 
+The 
+.B size
+type can be used to enfore a limit on an attribute length, and the
+.B count
+type limits the count of an attribute.
+
 Any attempt to add or modify an attribute named as part of the
 constraint overlay specification which does not fit the 
 constraint listed will fail with a
@@ -54,6 +62,8 @@ LDAP_CONSTRAINT_VIOLATION error.
 .RS
 .nf
 overlay constraint
+constraint_attribute jpegPhoto size 131072
+constraint_attribute userPassword count 3
 constraint_attribute mail regex ^[:alnum:]+@mydomain.com$
 constraint_attribute title uri
   ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
diff --git a/servers/slapd/overlays/constraint.c b/servers/slapd/overlays/constraint.c
index 8ce8a0fc6a..bb4d9504b1 100644
--- a/servers/slapd/overlays/constraint.c
+++ b/servers/slapd/overlays/constraint.c
@@ -41,6 +41,8 @@
 
 #define REGEX_STR "regex"
 #define URI_STR "uri"
+#define SIZE_STR "size"
+#define COUNT_STR "count"
 
 /*
  * Linked list of attribute constraints which we should enforce.
@@ -55,6 +57,8 @@ typedef struct constraint {
 	AttributeDescription *ap;
 	regex_t *re;
 	LDAPURLDesc *lud;
+	size_t size;
+	size_t count;
 	AttributeDescription **attrs;
 	struct berval val; /* constraint value */
 	struct berval dn;
@@ -129,6 +133,12 @@ constraint_cf_gen( ConfigArgs *c )
 				} else if (cp->lud) {
 					len += STRLENOF(URI_STR);
 					tstr = URI_STR;
+				} else if (cp->size) {
+					len += STRLENOF(SIZE_STR);
+					tstr = SIZE_STR;
+				} else if (cp->count) {
+					len += STRLENOF(COUNT_STR);
+					tstr = COUNT_STR;
 				}
 				len += cp->val.bv_len;
 
@@ -216,7 +226,17 @@ constraint_cf_gen( ConfigArgs *c )
 					return( ARG_BAD_CONF );
 				}
 				ber_str2bv( c->argv[3], 0, 1, &ap.val );
-			} else if ( strcasecmp( c->argv[2], URI_STR ) == 0) {
+			} else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) {
+				size_t size;
+
+				if ( ( size = atoi(c->argv[3]) ) != 0 )
+					ap.size = size;	
+			} else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) {
+				size_t count;
+
+				if ( ( count = atoi(c->argv[3]) ) != 0 )
+					ap.count = count;	
+			} else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) {
 				int err;
 			
 				err = ldap_url_parse(c->argv[3], &ap.lud);
@@ -281,6 +301,8 @@ constraint_cf_gen( ConfigArgs *c )
 			a2->re = ap.re;
 			a2->val = ap.val;
 			a2->lud = ap.lud;
+			a2->size = ap.size;
+			a2->count = ap.count;
 			if ( a2->lud ) {
 				ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
 				ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
@@ -323,6 +345,9 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply
 		(regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH))
 		return 1; /* regular expression violation */
 
+	if ((c->size) && (bv->bv_len > c->size))
+		return 1; /* size violation */
+
 	if (c->lud) {
 		Operation nop = *op;
 		slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
@@ -443,10 +468,21 @@ print_message( struct berval *errtext, AttributeDescription *a )
 	return ret;
 }
 
+static unsigned
+constraint_count_attr(Entry *e, AttributeDescription *ad)
+{
+	struct Attribute *a;
+
+	if ((a = attr_find(e->e_attrs, ad)) != NULL)
+		return a->a_numvals;
+	return 0;
+}
+
 static int
 constraint_add( Operation *op, SlapReply *rs )
 {
 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+	Backend *be = op->o_bd;
 	Attribute *a;
 	constraint *c = on->on_bi.bi_private, *cp;
 	BerVarray b = NULL;
@@ -469,35 +505,45 @@ constraint_add( Operation *op, SlapReply *rs )
 			if (cp->ap != a->a_desc) continue;
 			if ((b = a->a_vals) == NULL) continue;
 				
-			for(i=0; b[i].bv_val; i++) {
-				int cv = constraint_violation( cp, &b[i], op, rs);
-					
-				if (cv) {
-					/* violation */
-					op->o_bd->bd_info = (BackendInfo *)(on->on_info);
-					msg = print_message( &rsv, a->a_desc );
-					send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
-					ch_free(msg);
-					return (rs->sr_err);
-				}
-			}
+			Debug(LDAP_DEBUG_TRACE, 
+				"==> constraint_add, "
+				"a->a_numvals = %d, cp->count = %d\n",
+				a->a_numvals, cp->count, 0);
+
+			if ((cp->count != 0) && (a->a_numvals > cp->count))
+				goto add_violation;
+
+			for(i=0; b[i].bv_val; i++) 
+				if (constraint_violation( cp, &b[i], op, rs))
+					goto add_violation;
 		}
 	}
 	/* Default is to just fall through to the normal processing */
 	return SLAP_CB_CONTINUE;
+
+add_violation:
+	op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+	msg = print_message( &rsv, a->a_desc );
+	send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
+	ch_free(msg);
+	return (rs->sr_err);
 }
 
+
 static int
 constraint_modify( Operation *op, SlapReply *rs )
 {
 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+	Backend *be = op->o_bd;
 	constraint *c = on->on_bi.bi_private, *cp;
+	Entry *target_entry = NULL;
 	Modifications *m;
 	BerVarray b = NULL;
 	int i;
 	struct berval rsv = BER_BVC("modify breaks constraint");
 	char *msg;
 	
+	Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_modify()", 0,0,0);
 	if ((m = op->orm_modlist) == NULL) {
 		op->o_bd->bd_info = (BackendInfo *)(on->on_info);
 		send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
@@ -505,34 +551,96 @@ constraint_modify( Operation *op, SlapReply *rs )
 		return(rs->sr_err);
 	}
 
+	/* Do we need to count attributes? */
+	for(cp = c; cp; cp = cp->ap_next) {
+		if (cp->count != 0) {
+			int rc;
+
+			op->o_bd = on->on_info->oi_origdb;
+			rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
+			op->o_bd = be;
+
+			if (rc != 0 || target_entry == NULL) {
+				Debug(LDAP_DEBUG_TRACE, 
+					"==> constraint_modify rc = %d\n",
+					rc, 0, 0);
+				goto mod_violation;
+			}
+			break;
+		}
+	}
+		
 	for(;m; m = m->sml_next) {
+		int ce = 0;
+
+		/* Get this attribute count, if needed */
+		if (target_entry)
+			ce = constraint_count_attr(target_entry, m->sml_desc);
+
 		if (is_at_operational( m->sml_desc->ad_type )) continue;
 		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_REPLACE) &&
+			(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
 			continue;
 		/* we only care about ADD and REPLACE modifications */
+		/* and DELETE are used to track attribute count */
 		if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
 			continue;
 
 		for(cp = c; cp; cp = cp->ap_next) {
 			if (cp->ap != m->sml_desc) continue;
 			
-			for(i=0; b[i].bv_val; i++) {
-				int cv = constraint_violation( cp, &b[i], op, rs);
-				
-				if (cv) {
-					/* violation */
-					op->o_bd->bd_info = (BackendInfo *)(on->on_info);
-					msg = print_message( &rsv, m->sml_desc );
-					send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
-					ch_free(msg);
-					return (rs->sr_err);
+			if (cp->count != 0) {
+				int ca;
+
+				if (m->sml_op == LDAP_MOD_DELETE)
+					ce = 0;
+
+				for (ca = 0; b[ca].bv_val; ++ca);
+
+				Debug(LDAP_DEBUG_TRACE, 
+					"==> constraint_modify ce = %d, "
+					"ca = %d, cp->count = %d\n",
+					ce, ca, cp->count);
+
+				if (m->sml_op == LDAP_MOD_ADD)
+					if (ca + ce > cp->count)
+						goto mod_violation;
+				if (m->sml_op == LDAP_MOD_REPLACE) {
+					if (ca > cp->count)
+						goto mod_violation;
+					ce = ca;
 				}
-			}
+			} 
+
+			/* DELETE are to be ignored beyond this point */
+			if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
+				continue;
+
+			for(i=0; b[i].bv_val; i++)
+				if (constraint_violation( cp, &b[i], op, rs))
+					goto mod_violation;
 		}
 	}
 	
+	if (target_entry) {
+		op->o_bd = on->on_info->oi_origdb;
+		be_entry_release_r(op, target_entry);
+		op->o_bd = be;
+	}
 	return SLAP_CB_CONTINUE;
+mod_violation:
+	/* violation */
+	if (target_entry) {
+		op->o_bd = on->on_info->oi_origdb;
+		be_entry_release_r(op, target_entry);
+		op->o_bd = be;
+	}
+	op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+	msg = print_message( &rsv, m->sml_desc );
+	send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
+	ch_free(msg);
+	return (rs->sr_err);
 }
 
 static int
-- 
GitLab