1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/constraint.c,v 1.2.2.8 2008/05/27 19:59:47 quanah Exp $ */
2 /* constraint.c - Overlay to constrain attributes to certain values */
4 * Copyright 2003-2004 Hewlett-Packard Company
5 * Copyright 2007 Emmanuel Dreyfus
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * Authors: Neil Dunbar <neil.dunbar@hp.com>
18 * Emmannuel Dreyfus <manu@netbsd.org>
22 #ifdef SLAPD_OVER_CONSTRAINT
26 #include <ac/string.h>
27 #include <ac/socket.h>
35 * This overlay limits the values which can be placed into an
36 * attribute, over and above the limits placed by the schema.
38 * It traps only LDAP adds and modify commands (and only seeks to
39 * control the add and modify value mods of a modify)
42 #define REGEX_STR "regex"
44 #define SIZE_STR "size"
45 #define COUNT_STR "count"
48 * Linked list of attribute constraints which we should enforce.
49 * This is probably a sub optimal structure - some form of sorted
50 * array would be better if the number of attributes contrained is
51 * likely to be much bigger than 4 or 5. We stick with a list for
55 typedef struct constraint
{
56 struct constraint
*ap_next
;
57 AttributeDescription
*ap
;
62 AttributeDescription
**attrs
;
63 struct berval val
; /* constraint value */
69 CONSTRAINT_ATTRIBUTE
= 1
72 static ConfigDriver constraint_cf_gen
;
74 static ConfigTable constraintcfg
[] = {
75 { "constraint_attribute", "attribute> (regex|uri) <value",
76 4, 4, 0, ARG_MAGIC
| CONSTRAINT_ATTRIBUTE
, constraint_cf_gen
,
77 "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
78 "DESC 'regular expression constraint for attribute' "
79 "EQUALITY caseIgnoreMatch "
80 "SYNTAX OMsDirectoryString )", NULL
, NULL
},
81 { NULL
, NULL
, 0, 0, 0, ARG_IGNORED
}
84 static ConfigOCs constraintocs
[] = {
86 "NAME 'olcConstraintConfig' "
87 "DESC 'Constraint overlay configuration' "
88 "SUP olcOverlayConfig "
89 "MAY ( olcConstraintAttribute ) )",
90 Cft_Overlay
, constraintcfg
},
95 constraint_free( constraint
*cp
)
101 if (!BER_BVISNULL(&cp
->val
))
102 ch_free(cp
->val
.bv_val
);
104 ldap_free_urldesc(cp
->lud
);
111 constraint_cf_gen( ConfigArgs
*c
)
113 slap_overinst
*on
= (slap_overinst
*)(c
->bi
);
114 constraint
*cn
= on
->on_bi
.bi_private
, *cp
;
117 constraint ap
= { NULL
, NULL
, NULL
}, *a2
= NULL
;
118 const char *text
= NULL
;
121 case SLAP_CONFIG_EMIT
:
123 case CONSTRAINT_ATTRIBUTE
:
124 for (cp
=cn
; cp
; cp
=cp
->ap_next
) {
129 len
= cp
->ap
->ad_cname
.bv_len
+ 3;
131 len
+= STRLENOF(REGEX_STR
);
133 } else if (cp
->lud
) {
134 len
+= STRLENOF(URI_STR
);
136 } else if (cp
->size
) {
137 len
+= STRLENOF(SIZE_STR
);
139 } else if (cp
->count
) {
140 len
+= STRLENOF(COUNT_STR
);
143 len
+= cp
->val
.bv_len
;
147 bv
.bv_len
= snprintf(s
, len
, "%s %s %s", cp
->ap
->ad_cname
.bv_val
,
148 tstr
, cp
->val
.bv_val
);
150 rc
= value_add_one( &c
->rvalue_vals
, &bv
);
152 rc
= value_add_one( &c
->rvalue_nvals
, &bv
);
162 case LDAP_MOD_DELETE
:
164 case CONSTRAINT_ATTRIBUTE
:
165 if (!cn
) break; /* nothing to do */
168 /* zap all constraints */
171 constraint_free( cn
);
175 on
->on_bi
.bi_private
= NULL
;
179 /* zap constraint numbered 'valx' */
180 for(i
=0, cp
= cn
, cpp
= &cn
;
182 i
++, cpp
= &cp
->ap_next
, cp
= *cpp
);
185 /* zap cp, and join cpp to cp->ap_next */
187 constraint_free( cp
);
189 on
->on_bi
.bi_private
= cn
;
198 case SLAP_CONFIG_ADD
:
201 case CONSTRAINT_ATTRIBUTE
:
202 if ( slap_str2ad( c
->argv
[1], &ap
.ap
, &text
) ) {
203 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
204 "%s <%s>: %s\n", c
->argv
[0], c
->argv
[1], text
);
205 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
206 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
207 return( ARG_BAD_CONF
);
210 if ( strcasecmp( c
->argv
[2], REGEX_STR
) == 0) {
213 ap
.re
= ch_malloc( sizeof(regex_t
) );
214 if ((err
= regcomp( ap
.re
,
215 c
->argv
[3], REG_EXTENDED
)) != 0) {
218 regerror( err
, ap
.re
, errmsg
, sizeof(errmsg
) );
220 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
221 "%s %s: Illegal regular expression \"%s\": Error %s",
222 c
->argv
[0], c
->argv
[1], c
->argv
[3], errmsg
);
223 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
224 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
226 return( ARG_BAD_CONF
);
228 ber_str2bv( c
->argv
[3], 0, 1, &ap
.val
);
229 } else if ( strcasecmp( c
->argv
[2], SIZE_STR
) == 0 ) {
232 if ( ( size
= atoi(c
->argv
[3]) ) != 0 )
234 } else if ( strcasecmp( c
->argv
[2], COUNT_STR
) == 0 ) {
237 if ( ( count
= atoi(c
->argv
[3]) ) != 0 )
239 } else if ( strcasecmp( c
->argv
[2], URI_STR
) == 0 ) {
242 err
= ldap_url_parse(c
->argv
[3], &ap
.lud
);
243 if ( err
!= LDAP_URL_SUCCESS
) {
244 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
245 "%s %s: Invalid URI \"%s\"",
246 c
->argv
[0], c
->argv
[1], c
->argv
[3]);
247 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
248 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
249 return( ARG_BAD_CONF
);
252 if (ap
.lud
->lud_host
!= NULL
) {
253 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
254 "%s %s: unsupported hostname in URI \"%s\"",
255 c
->argv
[0], c
->argv
[1], c
->argv
[3]);
256 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
257 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
259 ldap_free_urldesc(ap
.lud
);
261 return( ARG_BAD_CONF
);
264 for ( i
=0; ap
.lud
->lud_attrs
[i
]; i
++);
265 /* FIXME: This is worthless without at least one attr */
267 ap
.attrs
= ch_malloc( (i
+1)*sizeof(AttributeDescription
*));
268 for ( i
=0; ap
.lud
->lud_attrs
[i
]; i
++) {
270 if ( slap_str2ad( ap
.lud
->lud_attrs
[i
], &ap
.attrs
[i
], &text
) ) {
272 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
273 "%s <%s>: %s\n", c
->argv
[0], ap
.lud
->lud_attrs
[i
], text
);
274 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
275 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
276 return( ARG_BAD_CONF
);
282 if (ap
.lud
->lud_dn
== NULL
)
283 ap
.lud
->lud_dn
= ch_strdup("");
285 if (ap
.lud
->lud_filter
== NULL
)
286 ap
.lud
->lud_filter
= ch_strdup("objectClass=*");
288 ber_str2bv( c
->argv
[3], 0, 1, &ap
.val
);
290 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
291 "%s %s: Unknown constraint type: %s",
292 c
->argv
[0], c
->argv
[1], c
->argv
[2] );
293 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
294 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
295 return ( ARG_BAD_CONF
);
298 a2
= ch_calloc( sizeof(constraint
), 1 );
299 a2
->ap_next
= on
->on_bi
.bi_private
;
305 a2
->count
= ap
.count
;
307 ber_str2bv(a2
->lud
->lud_dn
, 0, 0, &a2
->dn
);
308 ber_str2bv(a2
->lud
->lud_filter
, 0, 0, &a2
->filter
);
310 a2
->attrs
= ap
.attrs
;
311 on
->on_bi
.bi_private
= a2
;
326 constraint_uri_cb( Operation
*op
, SlapReply
*rs
)
328 if(rs
->sr_type
== REP_SEARCH
) {
329 int *foundp
= op
->o_callback
->sc_private
;
333 Debug(LDAP_DEBUG_TRACE
, "==> constraint_uri_cb <%s>\n",
334 rs
->sr_entry
? rs
->sr_entry
->e_name
.bv_val
: "UNKNOWN_DN", 0, 0);
340 constraint_violation( constraint
*c
, struct berval
*bv
, Operation
*op
, SlapReply
*rs
)
342 if ((!c
) || (!bv
)) return 0;
345 (regexec(c
->re
, bv
->bv_val
, 0, NULL
, 0) == REG_NOMATCH
))
346 return 1; /* regular expression violation */
348 if ((c
->size
) && (bv
->bv_len
> c
->size
))
349 return 1; /* size violation */
353 slap_overinst
*on
= (slap_overinst
*) op
->o_bd
->bd_info
;
355 SlapReply nrs
= { REP_RESULT
};
360 struct berval filterstr
;
369 cb
.sc_response
= constraint_uri_cb
;
370 cb
.sc_cleanup
= NULL
;
371 cb
.sc_private
= &found
;
373 nop
.o_protocol
= LDAP_VERSION3
;
374 nop
.o_tag
= LDAP_REQ_SEARCH
;
375 nop
.o_time
= slap_get_time();
376 if (c
->lud
->lud_dn
) {
379 ber_str2bv(c
->lud
->lud_dn
, 0, 0, &dn
);
382 nop
.o_bd
= select_backend(&nop
.o_req_ndn
, 1 );
383 if (!nop
.o_bd
|| !nop
.o_bd
->be_search
) {
384 return 1; /* unexpected error */
387 nop
.o_req_dn
= nop
.o_bd
->be_nsuffix
[0];
388 nop
.o_req_ndn
= nop
.o_bd
->be_nsuffix
[0];
389 nop
.o_bd
= on
->on_info
->oi_origdb
;
391 nop
.o_do_not_cache
= 1;
392 nop
.o_callback
= &cb
;
394 nop
.ors_scope
= c
->lud
->lud_scope
;
395 nop
.ors_deref
= LDAP_DEREF_NEVER
;
396 nop
.ors_slimit
= SLAP_NO_LIMIT
;
397 nop
.ors_tlimit
= SLAP_NO_LIMIT
;
398 nop
.ors_limit
= NULL
;
400 nop
.ors_attrsonly
= 0;
401 nop
.ors_attrs
= slap_anlist_no_attrs
;
403 len
= STRLENOF("(&(") +
407 for (i
= 0; c
->attrs
[i
]; i
++) {
408 len
+= STRLENOF("(") +
409 c
->attrs
[i
]->ad_cname
.bv_len
+
415 len
+= STRLENOF("))");
416 filterstr
.bv_len
= len
;
417 filterstr
.bv_val
= op
->o_tmpalloc(len
+ 1, op
->o_tmpmemctx
);
419 ptr
= filterstr
.bv_val
+
420 snprintf(filterstr
.bv_val
, len
, "(&(%s)(|", c
->lud
->lud_filter
);
421 for (i
= 0; c
->attrs
[i
]; i
++) {
423 ptr
= lutil_strcopy( ptr
, c
->attrs
[i
]->ad_cname
.bv_val
);
425 ptr
= lutil_strcopy( ptr
, bv
->bv_val
);
431 Debug(LDAP_DEBUG_TRACE
,
432 "==> constraint_violation uri filter = %s\n",
433 filterstr
.bv_val
, 0, 0);
435 nop
.ors_filterstr
= filterstr
;
436 nop
.ors_filter
= str2filter_x(&nop
, filterstr
.bv_val
);
438 rc
= nop
.o_bd
->be_search( &nop
, &nrs
);
440 op
->o_tmpfree(filterstr
.bv_val
, op
->o_tmpmemctx
);
441 Debug(LDAP_DEBUG_TRACE
,
442 "==> constraint_violation uri rc = %d, found = %d\n",
445 if((rc
!= LDAP_SUCCESS
) && (rc
!= LDAP_NO_SUCH_OBJECT
)) {
446 send_ldap_error(op
, rs
, rc
,
447 "constraint_violation uri search failed");
448 return 1; /* unexpected error */
452 return 1; /* constraint violation */
460 print_message( struct berval
*errtext
, AttributeDescription
*a
)
465 sz
= errtext
->bv_len
+ sizeof(" on ") + a
->ad_cname
.bv_len
;
467 snprintf( ret
, sz
, "%s on %s", errtext
->bv_val
, a
->ad_cname
.bv_val
);
472 constraint_count_attr(Entry
*e
, AttributeDescription
*ad
)
476 if ((a
= attr_find(e
->e_attrs
, ad
)) != NULL
)
482 constraint_add( Operation
*op
, SlapReply
*rs
)
484 slap_overinst
*on
= (slap_overinst
*) op
->o_bd
->bd_info
;
485 Backend
*be
= op
->o_bd
;
487 constraint
*c
= on
->on_bi
.bi_private
, *cp
;
490 struct berval rsv
= BER_BVC("add breaks constraint");
493 if ((a
= op
->ora_e
->e_attrs
) == NULL
) {
494 op
->o_bd
->bd_info
= (BackendInfo
*)(on
->on_info
);
495 send_ldap_error(op
, rs
, LDAP_INVALID_SYNTAX
,
496 "constraint_add() got null op.ora_e.e_attrs");
500 for(; a
; a
= a
->a_next
) {
501 /* we don't constrain operational attributes */
502 if (is_at_operational(a
->a_desc
->ad_type
)) continue;
504 for(cp
= c
; cp
; cp
= cp
->ap_next
) {
505 if (cp
->ap
!= a
->a_desc
) continue;
506 if ((b
= a
->a_vals
) == NULL
) continue;
508 Debug(LDAP_DEBUG_TRACE
,
509 "==> constraint_add, "
510 "a->a_numvals = %d, cp->count = %d\n",
511 a
->a_numvals
, cp
->count
, 0);
513 if ((cp
->count
!= 0) && (a
->a_numvals
> cp
->count
))
516 for(i
=0; b
[i
].bv_val
; i
++)
517 if (constraint_violation( cp
, &b
[i
], op
, rs
))
521 /* Default is to just fall through to the normal processing */
522 return SLAP_CB_CONTINUE
;
525 op
->o_bd
->bd_info
= (BackendInfo
*)(on
->on_info
);
526 msg
= print_message( &rsv
, a
->a_desc
);
527 send_ldap_error(op
, rs
, LDAP_CONSTRAINT_VIOLATION
, msg
);
534 constraint_modify( Operation
*op
, SlapReply
*rs
)
536 slap_overinst
*on
= (slap_overinst
*) op
->o_bd
->bd_info
;
537 Backend
*be
= op
->o_bd
;
538 constraint
*c
= on
->on_bi
.bi_private
, *cp
;
539 Entry
*target_entry
= NULL
;
543 struct berval rsv
= BER_BVC("modify breaks constraint");
546 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
, "constraint_modify()", 0,0,0);
547 if ((m
= op
->orm_modlist
) == NULL
) {
548 op
->o_bd
->bd_info
= (BackendInfo
*)(on
->on_info
);
549 send_ldap_error(op
, rs
, LDAP_INVALID_SYNTAX
,
550 "constraint_modify() got null orm_modlist");
554 /* Do we need to count attributes? */
555 for(cp
= c
; cp
; cp
= cp
->ap_next
) {
556 if (cp
->count
!= 0) {
559 op
->o_bd
= on
->on_info
->oi_origdb
;
560 rc
= be_entry_get_rw( op
, &op
->o_req_ndn
, NULL
, NULL
, 0, &target_entry
);
563 if (rc
!= 0 || target_entry
== NULL
) {
564 Debug(LDAP_DEBUG_TRACE
,
565 "==> constraint_modify rc = %d\n",
573 for(;m
; m
= m
->sml_next
) {
576 /* Get this attribute count, if needed */
578 ce
= constraint_count_attr(target_entry
, m
->sml_desc
);
580 if (is_at_operational( m
->sml_desc
->ad_type
)) continue;
581 if ((( m
->sml_op
& LDAP_MOD_OP
) != LDAP_MOD_ADD
) &&
582 (( m
->sml_op
& LDAP_MOD_OP
) != LDAP_MOD_REPLACE
) &&
583 (( m
->sml_op
& LDAP_MOD_OP
) != LDAP_MOD_DELETE
))
585 /* we only care about ADD and REPLACE modifications */
586 /* and DELETE are used to track attribute count */
587 if ((( b
= m
->sml_values
) == NULL
) || (b
[0].bv_val
== NULL
))
590 for(cp
= c
; cp
; cp
= cp
->ap_next
) {
591 if (cp
->ap
!= m
->sml_desc
) continue;
593 if (cp
->count
!= 0) {
596 if (m
->sml_op
== LDAP_MOD_DELETE
)
599 for (ca
= 0; b
[ca
].bv_val
; ++ca
);
601 Debug(LDAP_DEBUG_TRACE
,
602 "==> constraint_modify ce = %d, "
603 "ca = %d, cp->count = %d\n",
606 if (m
->sml_op
== LDAP_MOD_ADD
)
607 if (ca
+ ce
> cp
->count
)
609 if (m
->sml_op
== LDAP_MOD_REPLACE
) {
616 /* DELETE are to be ignored beyond this point */
617 if (( m
->sml_op
& LDAP_MOD_OP
) == LDAP_MOD_DELETE
)
620 for(i
=0; b
[i
].bv_val
; i
++)
621 if (constraint_violation( cp
, &b
[i
], op
, rs
))
627 op
->o_bd
= on
->on_info
->oi_origdb
;
628 be_entry_release_r(op
, target_entry
);
631 return SLAP_CB_CONTINUE
;
635 op
->o_bd
= on
->on_info
->oi_origdb
;
636 be_entry_release_r(op
, target_entry
);
639 op
->o_bd
->bd_info
= (BackendInfo
*)(on
->on_info
);
640 msg
= print_message( &rsv
, m
->sml_desc
);
641 send_ldap_error(op
, rs
, LDAP_CONSTRAINT_VIOLATION
, msg
);
651 slap_overinst
*on
= (slap_overinst
*) be
->bd_info
;
654 for ( ap
= on
->on_bi
.bi_private
; ap
; ap
= a2
) {
656 constraint_free( ap
);
662 static slap_overinst constraint_ovl
;
664 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
668 constraint_initialize( void ) {
671 constraint_ovl
.on_bi
.bi_type
= "constraint";
672 constraint_ovl
.on_bi
.bi_db_close
= constraint_close
;
673 constraint_ovl
.on_bi
.bi_op_add
= constraint_add
;
674 constraint_ovl
.on_bi
.bi_op_modify
= constraint_modify
;
676 constraint_ovl
.on_bi
.bi_private
= NULL
;
678 constraint_ovl
.on_bi
.bi_cf_ocs
= constraintocs
;
679 rc
= config_register_schema( constraintcfg
, constraintocs
);
682 return overlay_register( &constraint_ovl
);
685 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
686 int init_module(int argc
, char *argv
[]) {
687 return constraint_initialize();
691 #endif /* defined(SLAPD_OVER_CONSTRAINT) */