]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/constraint.c
ITS#7780,#7781 fix prev commit
[openldap] / servers / slapd / overlays / constraint.c
1 /* $OpenLDAP$ */
2 /* constraint.c - Overlay to constrain attributes to certain values */
3 /* 
4  * Copyright 2003-2004 Hewlett-Packard Company
5  * Copyright 2007 Emmanuel Dreyfus
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
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>.
15  */
16 /*
17  * Authors: Neil Dunbar <neil.dunbar@hp.com>
18  *                      Emmannuel Dreyfus <manu@netbsd.org>
19  */
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_CONSTRAINT
23
24 #include <stdio.h>
25
26 #include <ac/string.h>
27 #include <ac/socket.h>
28 #include <ac/regex.h>
29
30 #include "lutil.h"
31 #include "slap.h"
32 #include "config.h"
33
34 /*
35  * This overlay limits the values which can be placed into an
36  * attribute, over and above the limits placed by the schema.
37  *
38  * It traps only LDAP adds and modify commands (and only seeks to
39  * control the add and modify value mods of a modify)
40  */
41
42 #define REGEX_STR "regex"
43 #define URI_STR "uri"
44 #define SET_STR "set"
45 #define SIZE_STR "size"
46 #define COUNT_STR "count"
47
48 /*
49  * Linked list of attribute constraints which we should enforce.
50  * This is probably a sub optimal structure - some form of sorted
51  * array would be better if the number of attributes contrained is
52  * likely to be much bigger than 4 or 5. We stick with a list for
53  * the moment.
54  */
55
56 typedef struct constraint {
57         struct constraint *ap_next;
58         AttributeDescription **ap;
59
60         LDAPURLDesc *restrict_lud;
61         struct berval restrict_ndn;
62         Filter *restrict_filter;
63         struct berval restrict_val;
64
65         int type;
66         regex_t *re;
67         LDAPURLDesc *lud;
68         int set;
69         size_t size;
70         size_t count;
71         AttributeDescription **attrs;
72         struct berval val; /* constraint value */
73         struct berval dn;
74         struct berval filter;
75 } constraint;
76
77 enum {
78         CONSTRAINT_ATTRIBUTE = 1,
79         CONSTRAINT_COUNT,
80         CONSTRAINT_SIZE,
81         CONSTRAINT_REGEX,
82         CONSTRAINT_SET,
83         CONSTRAINT_URI,
84 };
85
86 static ConfigDriver constraint_cf_gen;
87
88 static ConfigTable constraintcfg[] = {
89         { "constraint_attribute", "attribute[list]> (regex|uri|set|size|count) <value> [<restrict URI>]",
90           4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
91           "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
92           "DESC 'constraint for list of attributes' "
93           "EQUALITY caseIgnoreMatch "
94           "SYNTAX OMsDirectoryString )", NULL, NULL },
95         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
96 };
97
98 static ConfigOCs constraintocs[] = {
99         { "( OLcfgOvOc:13.1 "
100           "NAME 'olcConstraintConfig' "
101           "DESC 'Constraint overlay configuration' "
102           "SUP olcOverlayConfig "
103           "MAY ( olcConstraintAttribute ) )",
104           Cft_Overlay, constraintcfg },
105         { NULL, 0, NULL }
106 };
107
108 static void
109 constraint_free( constraint *cp, int freeme )
110 {
111         if (cp->restrict_lud)
112                 ldap_free_urldesc(cp->restrict_lud);
113         if (!BER_BVISNULL(&cp->restrict_ndn))
114                 ch_free(cp->restrict_ndn.bv_val);
115         if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres)
116                 filter_free(cp->restrict_filter);
117         if (!BER_BVISNULL(&cp->restrict_val))
118                 ch_free(cp->restrict_val.bv_val);
119         if (cp->re) {
120                 regfree(cp->re);
121                 ch_free(cp->re);
122         }
123         if (!BER_BVISNULL(&cp->val))
124                 ch_free(cp->val.bv_val);
125         if (cp->lud)
126                 ldap_free_urldesc(cp->lud);
127         if (cp->attrs)
128                 ch_free(cp->attrs);
129         if (cp->ap)
130                 ch_free(cp->ap);
131         if (freeme)
132                 ch_free(cp);
133 }
134
135 static int
136 constraint_cf_gen( ConfigArgs *c )
137 {
138         slap_overinst *on = (slap_overinst *)(c->bi);
139         constraint *cn = on->on_bi.bi_private, *cp;
140         struct berval bv;
141         int i, rc = 0;
142         constraint ap = { NULL };
143         const char *text = NULL;
144         
145         switch ( c->op ) {
146         case SLAP_CONFIG_EMIT:
147                 switch (c->type) {
148                 case CONSTRAINT_ATTRIBUTE:
149                         for (cp=cn; cp; cp=cp->ap_next) {
150                                 char *s;
151                                 char *tstr = NULL;
152                                 int quotes = 0, numeric = 0;
153                                 int j;
154                                 size_t val;
155                                 char val_buf[SLAP_TEXT_BUFLEN] = { '\0' };
156
157                                 bv.bv_len = STRLENOF("  ");
158                                 for (j = 0; cp->ap[j]; j++) {
159                                         bv.bv_len += cp->ap[j]->ad_cname.bv_len;
160                                 }
161
162                                 /* room for commas */
163                                 bv.bv_len += j - 1;
164
165                                 switch (cp->type) {
166                                         case CONSTRAINT_COUNT:
167                                                 tstr = COUNT_STR;
168                                                 val = cp->count;
169                                                 numeric = 1;
170                                                 break;
171                                         case CONSTRAINT_SIZE:
172                                                 tstr = SIZE_STR;
173                                                 val = cp->size;
174                                                 numeric = 1;
175                                                 break;
176                                         case CONSTRAINT_REGEX:
177                                                 tstr = REGEX_STR;
178                                                 quotes = 1;
179                                                 break;
180                                         case CONSTRAINT_SET:
181                                                 tstr = SET_STR;
182                                                 quotes = 1;
183                                                 break;
184                                         case CONSTRAINT_URI:
185                                                 tstr = URI_STR;
186                                                 quotes = 1;
187                                                 break;
188                                         default:
189                                                 abort();
190                                 }
191
192                                 bv.bv_len += strlen(tstr);
193                                 bv.bv_len += cp->val.bv_len + 2*quotes;
194
195                                 if (cp->restrict_lud != NULL) {
196                                         bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\"");
197                                 }
198
199                                 if (numeric) {
200                                         int len = snprintf(val_buf, sizeof(val_buf), "%zu", val);
201                                         if (len <= 0) {
202                                                 /* error */
203                                                 return -1;
204                                         }
205                                         bv.bv_len += len;
206                                 }
207
208                                 s = bv.bv_val = ch_malloc(bv.bv_len + 1);
209
210                                 s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len );
211                                 for (j = 1; cp->ap[j]; j++) {
212                                         *s++ = ',';
213                                         s = lutil_strncopy( s, cp->ap[j]->ad_cname.bv_val, cp->ap[j]->ad_cname.bv_len );
214                                 }
215                                 *s++ = ' ';
216                                 s = lutil_strcopy( s, tstr );
217                                 *s++ = ' ';
218                                 if (numeric) {
219                                         s = lutil_strcopy( s, val_buf );
220                                 } else {
221                                         if ( quotes ) *s++ = '"';
222                                         s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
223                                         if ( quotes ) *s++ = '"';
224                                 }
225                                 if (cp->restrict_lud != NULL) {
226                                         s = lutil_strcopy( s, " restrict=\"" );
227                                         s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len );
228                                         *s++ = '"';
229                                 }
230                                 *s = '\0';
231
232                                 rc = value_add_one( &c->rvalue_vals, &bv );
233                                 if (rc == LDAP_SUCCESS)
234                                         rc = value_add_one( &c->rvalue_nvals, &bv );
235                                 ch_free(bv.bv_val);
236                                 if (rc) return rc;
237                         }
238                         break;
239                 default:
240                         abort();
241                         break;
242                 }
243                 break;
244         case LDAP_MOD_DELETE:
245                 switch (c->type) {
246                 case CONSTRAINT_ATTRIBUTE:
247                         if (!cn) break; /* nothing to do */
248                                         
249                         if (c->valx < 0) {
250                                 /* zap all constraints */
251                                 while (cn) {
252                                         cp = cn->ap_next;
253                                         constraint_free( cn, 1 );
254                                         cn = cp;
255                                 }
256                                                 
257                                 on->on_bi.bi_private = NULL;
258                         } else {
259                                 constraint **cpp;
260                                                 
261                                 /* zap constraint numbered 'valx' */
262                                 for(i=0, cp = cn, cpp = &cn;
263                                         (cp) && (i<c->valx);
264                                         i++, cpp = &cp->ap_next, cp = *cpp);
265
266                                 if (cp) {
267                                         /* zap cp, and join cpp to cp->ap_next */
268                                         *cpp = cp->ap_next;
269                                         constraint_free( cp, 1 );
270                                 }
271                                 on->on_bi.bi_private = cn;
272                         }
273                         break;
274
275                 default:
276                         abort();
277                         break;
278                 }
279                 break;
280         case SLAP_CONFIG_ADD:
281         case LDAP_MOD_ADD:
282                 switch (c->type) {
283                 case CONSTRAINT_ATTRIBUTE: {
284                         int j;
285                         char **attrs = ldap_str2charray( c->argv[1], "," );
286
287                         for ( j = 0; attrs[j]; j++)
288                                 /* just count */ ;
289                         ap.ap = ch_calloc( sizeof(AttributeDescription*), j + 1 );
290                         for ( j = 0; attrs[j]; j++) {
291                                 if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) {
292                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
293                                                 "%s <%s>: %s\n", c->argv[0], attrs[j], text );
294                                         rc = ARG_BAD_CONF;
295                                         goto done;
296                                 }
297                         }
298
299                         if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) {
300                                 int err;
301                         
302                                 ap.type = CONSTRAINT_REGEX;
303                                 ap.re = ch_malloc( sizeof(regex_t) );
304                                 if ((err = regcomp( ap.re,
305                                         c->argv[3], REG_EXTENDED )) != 0) {
306                                         char errmsg[1024];
307                                                         
308                                         regerror( err, ap.re, errmsg, sizeof(errmsg) );
309                                         ch_free(ap.re);
310                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
311                                                 "%s %s: Illegal regular expression \"%s\": Error %s",
312                                                 c->argv[0], c->argv[1], c->argv[3], errmsg);
313                                         ap.re = NULL;
314                                         rc = ARG_BAD_CONF;
315                                         goto done;
316                                 }
317                                 ber_str2bv( c->argv[3], 0, 1, &ap.val );
318                         } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) {
319                                 size_t size;
320                                 char *endptr;
321
322                                 ap.type = CONSTRAINT_SIZE;
323                                 ap.size = strtoull(c->argv[3], &endptr, 10);
324                                 if ( *endptr )
325                                         rc = ARG_BAD_CONF;
326                         } else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) {
327                                 size_t count;
328                                 char *endptr;
329
330                                 ap.type = CONSTRAINT_COUNT;
331                                 ap.count = strtoull(c->argv[3], &endptr, 10);
332                                 if ( *endptr )
333                                         rc = ARG_BAD_CONF;
334                         } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) {
335                                 int err;
336                         
337                                 ap.type = CONSTRAINT_URI;
338                                 err = ldap_url_parse(c->argv[3], &ap.lud);
339                                 if ( err != LDAP_URL_SUCCESS ) {
340                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
341                                                 "%s %s: Invalid URI \"%s\"",
342                                                 c->argv[0], c->argv[1], c->argv[3]);
343                                         rc = ARG_BAD_CONF;
344                                         goto done;
345                                 }
346
347                                 if (ap.lud->lud_host != NULL) {
348                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
349                                                 "%s %s: unsupported hostname in URI \"%s\"",
350                                                 c->argv[0], c->argv[1], c->argv[3]);
351                                         ldap_free_urldesc(ap.lud);
352                                         rc = ARG_BAD_CONF;
353                                         goto done;
354                                 }
355
356                                 for ( i=0; ap.lud->lud_attrs[i]; i++);
357                                 /* FIXME: This is worthless without at least one attr */
358                                 if ( i ) {
359                                         ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *));
360                                         for ( i=0; ap.lud->lud_attrs[i]; i++) {
361                                                 ap.attrs[i] = NULL;
362                                                 if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) {
363                                                         ch_free( ap.attrs );
364                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
365                                                                 "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text );
366                                                         rc = ARG_BAD_CONF;
367                                                         goto done;
368                                                 }
369                                         }
370                                         ap.attrs[i] = NULL;
371                                 }
372
373                                 if (ap.lud->lud_dn == NULL) {
374                                         ap.lud->lud_dn = ch_strdup("");
375                                 } else {
376                                         struct berval dn, ndn;
377
378                                         ber_str2bv( ap.lud->lud_dn, 0, 0, &dn );
379                                         if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) {
380                                                 /* cleanup */
381                                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
382                                                         "%s %s: URI %s DN normalization failed",
383                                                         c->argv[0], c->argv[1], c->argv[3] );
384                                                 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
385                                                            "%s: %s\n", c->log, c->cr_msg, 0 );
386                                                 rc = ARG_BAD_CONF;
387                                                 goto done;
388                                         }
389                                         ldap_memfree( ap.lud->lud_dn );
390                                         ap.lud->lud_dn = ndn.bv_val;
391                                 }
392
393                                 if (ap.lud->lud_filter == NULL) {
394                                         ap.lud->lud_filter = ch_strdup("objectClass=*");
395                                 } else if ( ap.lud->lud_filter[0] == '(' ) {
396                                         ber_len_t len = strlen( ap.lud->lud_filter );
397                                         if ( ap.lud->lud_filter[len - 1] != ')' ) {
398                                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
399                                                         "%s %s: invalid URI filter: %s",
400                                                         c->argv[0], c->argv[1], ap.lud->lud_filter );
401                                                 rc = ARG_BAD_CONF;
402                                                 goto done;
403                                         }
404                                         AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 );
405                                         ap.lud->lud_filter[len - 2] = '\0';
406                                 }
407
408                                 ber_str2bv( c->argv[3], 0, 1, &ap.val );
409
410                         } else if ( strcasecmp( c->argv[2], SET_STR ) == 0 ) {
411                                 ap.set = 1;
412                                 ber_str2bv( c->argv[3], 0, 1, &ap.val );
413                                 ap.type = CONSTRAINT_SET;
414
415                         } else {
416                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
417                                         "%s %s: Unknown constraint type: %s",
418                                         c->argv[0], c->argv[1], c->argv[2] );
419                                 rc = ARG_BAD_CONF;
420                                 goto done;
421                         }
422
423                         if ( c->argc > 4 ) {
424                                 int argidx;
425
426                                 for ( argidx = 4; argidx < c->argc; argidx++ ) {
427                                         if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) {
428                                                 int err;
429                                                 char *arg = c->argv[argidx] + STRLENOF("restrict=");
430
431                                                 err = ldap_url_parse(arg, &ap.restrict_lud);
432                                                 if ( err != LDAP_URL_SUCCESS ) {
433                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
434                                                                 "%s %s: Invalid restrict URI \"%s\"",
435                                                                 c->argv[0], c->argv[1], arg);
436                                                         rc = ARG_BAD_CONF;
437                                                         goto done;
438                                                 }
439
440                                                 if (ap.restrict_lud->lud_host != NULL) {
441                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
442                                                                 "%s %s: unsupported hostname in restrict URI \"%s\"",
443                                                                 c->argv[0], c->argv[1], arg);
444                                                         rc = ARG_BAD_CONF;
445                                                         goto done;
446                                                 }
447
448                                                 if ( ap.restrict_lud->lud_attrs != NULL ) {
449                                                         if ( ap.restrict_lud->lud_attrs[0] != '\0' ) {
450                                                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
451                                                                         "%s %s: attrs not allowed in restrict URI %s\n",
452                                                                         c->argv[0], c->argv[1], arg);
453                                                                 rc = ARG_BAD_CONF;
454                                                                 goto done;
455                                                         }
456                                                         ldap_memvfree((void *)ap.restrict_lud->lud_attrs);
457                                                         ap.restrict_lud->lud_attrs = NULL;
458                                                 }
459
460                                                 if (ap.restrict_lud->lud_dn != NULL) {
461                                                         if (ap.restrict_lud->lud_dn[0] == '\0') {
462                                                                 ldap_memfree(ap.restrict_lud->lud_dn);
463                                                                 ap.restrict_lud->lud_dn = NULL;
464
465                                                         } else {
466                                                                 struct berval dn, ndn;
467                                                                 int j;
468
469                                                                 ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn);
470                                                                 if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) {
471                                                                         /* cleanup */
472                                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
473                                                                                 "%s %s: restrict URI %s DN normalization failed",
474                                                                                 c->argv[0], c->argv[1], arg );
475                                                                         rc = ARG_BAD_CONF;
476                                                                         goto done;
477                                                                 }
478
479                                                                 assert(c->be != NULL);
480                                                                 if (c->be->be_nsuffix == NULL) {
481                                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
482                                                                                 "%s %s: restrict URI requires suffix",
483                                                                                 c->argv[0], c->argv[1] );
484                                                                         rc = ARG_BAD_CONF;
485                                                                         goto done;
486                                                                 }
487
488                                                                 for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) {
489                                                                         if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break;
490                                                                 }
491
492                                                                 if (BER_BVISNULL(&c->be->be_nsuffix[j])) {
493                                                                         /* error */
494                                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
495                                                                                 "%s %s: restrict URI DN %s not within database naming context(s)",
496                                                                                 c->argv[0], c->argv[1], dn.bv_val );
497                                                                         rc = ARG_BAD_CONF;
498                                                                         goto done;
499                                                                 }
500
501                                                                 ap.restrict_ndn = ndn;
502                                                         }
503                                                 }
504
505                                                 if (ap.restrict_lud->lud_filter != NULL) {
506                                                         ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter);
507                                                         if (ap.restrict_filter == NULL) {
508                                                                 /* error */
509                                                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
510                                                                         "%s %s: restrict URI filter %s invalid",
511                                                                         c->argv[0], c->argv[1], ap.restrict_lud->lud_filter );
512                                                                 rc = ARG_BAD_CONF;
513                                                                 goto done;
514                                                         }
515                                                 }
516
517                                                 ber_str2bv(c->argv[argidx] + STRLENOF("restrict="), 0, 1, &ap.restrict_val);
518
519                                         } else {
520                                                 /* cleanup */
521                                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
522                                                         "%s %s: unrecognized arg #%d (%s)",
523                                                         c->argv[0], c->argv[1], argidx, c->argv[argidx] );
524                                                 rc = ARG_BAD_CONF;
525                                                 goto done;
526                                         }
527                                 }
528                         }
529
530 done:;
531                         if ( rc == LDAP_SUCCESS ) {
532                                 constraint *a2 = ch_calloc( sizeof(constraint), 1 );
533                                 a2->ap_next = on->on_bi.bi_private;
534                                 a2->ap = ap.ap;
535                                 a2->type = ap.type;
536                                 a2->re = ap.re;
537                                 a2->val = ap.val;
538                                 a2->lud = ap.lud;
539                                 a2->set = ap.set;
540                                 a2->size = ap.size;
541                                 a2->count = ap.count;
542                                 if ( a2->lud ) {
543                                         ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
544                                         ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
545                                 }
546                                 a2->attrs = ap.attrs;
547                                 a2->restrict_lud = ap.restrict_lud;
548                                 a2->restrict_ndn = ap.restrict_ndn;
549                                 a2->restrict_filter = ap.restrict_filter;
550                                 a2->restrict_val = ap.restrict_val;
551                                 on->on_bi.bi_private = a2;
552
553                         } else {
554                                 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
555                                            "%s: %s\n", c->log, c->cr_msg, 0 );
556                                 constraint_free( &ap, 0 );
557                         }
558
559                         ldap_memvfree((void**)attrs);
560                         } break;
561                 default:
562                         abort();
563                         break;
564                 }
565                 break;
566         default:
567                 abort();
568         }
569
570         return rc;
571 }
572
573 static int
574 constraint_uri_cb( Operation *op, SlapReply *rs ) 
575 {
576         if(rs->sr_type == REP_SEARCH) {
577                 int *foundp = op->o_callback->sc_private;
578
579                 *foundp = 1;
580
581                 Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n",
582                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
583         }
584         return 0;
585 }
586
587 static int
588 constraint_violation( constraint *c, struct berval *bv, Operation *op )
589 {
590         if ((!c) || (!bv)) return LDAP_SUCCESS;
591         
592         switch (c->type) {
593                 case CONSTRAINT_SIZE:
594                         if (bv->bv_len > c->size)
595                                 return LDAP_CONSTRAINT_VIOLATION; /* size violation */
596                         break;
597                 case CONSTRAINT_REGEX:
598                         if (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH)
599                                 return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */
600                         break;
601                 case CONSTRAINT_URI: {
602                         Operation nop = *op;
603                         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
604                         slap_callback cb;
605                         int i;
606                         int found = 0;
607                         int rc;
608                         size_t len;
609                         struct berval filterstr;
610                         char *ptr;
611
612                         cb.sc_next = NULL;
613                         cb.sc_response = constraint_uri_cb;
614                         cb.sc_cleanup = NULL;
615                         cb.sc_private = &found;
616
617                         nop.o_protocol = LDAP_VERSION3;
618                         nop.o_tag = LDAP_REQ_SEARCH;
619                         nop.o_time = slap_get_time();
620                         if (c->lud->lud_dn) {
621                                 struct berval dn;
622
623                                 ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
624                                 nop.o_req_dn = dn;
625                                 nop.o_req_ndn = dn;
626                                 nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
627                                 if (!nop.o_bd) {
628                                         return LDAP_NO_SUCH_OBJECT; /* unexpected error */
629                                 }
630                                 if (!nop.o_bd->be_search) {
631                                         return LDAP_OTHER; /* unexpected error */
632                                 }
633                         } else {
634                                 nop.o_req_dn = nop.o_bd->be_nsuffix[0];
635                                 nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
636                                 nop.o_bd = on->on_info->oi_origdb;
637                         }
638                         nop.o_do_not_cache = 1;
639                         nop.o_callback = &cb;
640
641                         nop.ors_scope = c->lud->lud_scope;
642                         nop.ors_deref = LDAP_DEREF_NEVER;
643                         nop.ors_slimit = SLAP_NO_LIMIT;
644                         nop.ors_tlimit = SLAP_NO_LIMIT;
645                         nop.ors_limit = NULL;
646
647                         nop.ors_attrsonly = 0;
648                         nop.ors_attrs = slap_anlist_no_attrs;
649
650                         len = STRLENOF("(&(") +
651                                   c->filter.bv_len +
652                                   STRLENOF(")(|");
653
654                         for (i = 0; c->attrs[i]; i++) {
655                                 len += STRLENOF("(") +
656                                            c->attrs[i]->ad_cname.bv_len +
657                                            STRLENOF("=") +
658                                            bv->bv_len +
659                                            STRLENOF(")");
660                         }
661
662                         len += STRLENOF("))");
663                         filterstr.bv_len = len;
664                         filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
665
666                         ptr = filterstr.bv_val +
667                                 snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
668                         for (i = 0; c->attrs[i]; i++) {
669                                 *ptr++ = '(';
670                                 ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
671                                 *ptr++ = '=';
672                                 ptr = lutil_strcopy( ptr, bv->bv_val );
673                                 *ptr++ = ')';
674                         }
675                         *ptr++ = ')';
676                         *ptr++ = ')';
677                         *ptr++ = '\0';
678
679                         nop.ors_filterstr = filterstr;
680                         nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
681                         if ( nop.ors_filter == NULL ) {
682                                 Debug( LDAP_DEBUG_ANY,
683                                         "%s constraint_violation uri filter=\"%s\" invalid\n",
684                                         op->o_log_prefix, filterstr.bv_val, 0 );
685                                 rc = LDAP_OTHER;
686
687                         } else {
688                                 SlapReply nrs = { REP_RESULT };
689
690                                 Debug(LDAP_DEBUG_TRACE,
691                                         "==> constraint_violation uri filter = %s\n",
692                                         filterstr.bv_val, 0, 0);
693
694                                 rc = nop.o_bd->be_search( &nop, &nrs );
695
696                                 Debug(LDAP_DEBUG_TRACE,
697                                         "==> constraint_violation uri rc = %d, found = %d\n",
698                                         rc, found, 0);
699                         }
700                         op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
701
702                         if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
703                                 return rc; /* unexpected error */
704                         }
705
706                         if (!found)
707                                 return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
708                         break;
709                 }
710         }
711
712         return LDAP_SUCCESS;
713 }
714
715 static char *
716 print_message( struct berval *errtext, AttributeDescription *a )
717 {
718         char *ret;
719         int sz;
720         
721         sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
722         ret = ch_malloc(sz);
723         snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
724         return ret;
725 }
726
727 static unsigned
728 constraint_count_attr(Entry *e, AttributeDescription *ad)
729 {
730         struct Attribute *a;
731
732         if ((a = attr_find(e->e_attrs, ad)) != NULL)
733                 return a->a_numvals;
734         return 0;
735 }
736
737 static int
738 constraint_check_restrict( Operation *op, constraint *c, Entry *e )
739 {
740         assert( c->restrict_lud != NULL );
741
742         if ( c->restrict_lud->lud_dn != NULL ) {
743                 int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len;
744
745                 if ( diff < 0 ) {
746                         return 0;
747                 }
748
749                 if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) {
750                         return bvmatch( &e->e_nname, &c->restrict_ndn );
751                 }
752
753                 if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) {
754                         return 0;
755                 }
756
757                 if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) {
758                         struct berval pdn;
759
760                         if ( diff == 0 ) {
761                                 return 0;
762                         }
763
764                         dnParent( &e->e_nname, &pdn );
765
766                         if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL
767                                 && pdn.bv_len != c->restrict_ndn.bv_len )
768                         {
769                                 return 0;
770                         }
771                 }
772         }
773
774         if ( c->restrict_filter != NULL ) {
775                 int rc;
776                 struct berval save_dn = op->o_dn, save_ndn = op->o_ndn;
777
778                 op->o_dn = op->o_bd->be_rootdn;
779                 op->o_ndn = op->o_bd->be_rootndn;
780                 rc = test_filter( op, e, c->restrict_filter );
781                 op->o_dn = save_dn;
782                 op->o_ndn = save_ndn;
783
784                 if ( rc != LDAP_COMPARE_TRUE ) {
785                         return 0;
786                 }
787         }
788
789         return 1;
790 }
791
792 static int
793 constraint_add( Operation *op, SlapReply *rs )
794 {
795         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
796         Attribute *a;
797         constraint *c = on->on_bi.bi_private, *cp;
798         BerVarray b = NULL;
799         int i;
800         struct berval rsv = BER_BVC("add breaks constraint");
801         int rc = 0;
802         char *msg = NULL;
803
804         if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
805                 return SLAP_CB_CONTINUE;
806         }
807
808         if ((a = op->ora_e->e_attrs) == NULL) {
809                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
810                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
811                         "constraint_add: no attrs");
812                 return(rs->sr_err);
813         }
814
815         for(; a; a = a->a_next ) {
816                 /* we don't constrain operational attributes */
817                 if (is_at_operational(a->a_desc->ad_type)) continue;
818
819                 for(cp = c; cp; cp = cp->ap_next) {
820                         int j;
821                         for (j = 0; cp->ap[j]; j++) {
822                                 if (cp->ap[j] == a->a_desc) break;
823                         }
824                         if (cp->ap[j] == NULL) continue;
825                         if ((b = a->a_vals) == NULL) continue;
826
827                         if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) {
828                                 continue;
829                         }
830
831                         Debug(LDAP_DEBUG_TRACE, 
832                                 "==> constraint_add, "
833                                 "a->a_numvals = %u, cp->count = %lu\n",
834                                 a->a_numvals, (unsigned long) cp->count, 0);
835
836                         switch (cp->type) {
837                                 case CONSTRAINT_COUNT:
838                                         if (a->a_numvals > cp->count)
839                                                 rc = LDAP_CONSTRAINT_VIOLATION;
840                                         break;
841                                 case CONSTRAINT_SET:
842                                         if (acl_match_set(&cp->val, op, op->ora_e, NULL) == 0)
843                                                 rc = LDAP_CONSTRAINT_VIOLATION;
844                                         break;
845                                 default:
846                                         for ( i = 0; b[i].bv_val; i++ ) {
847                                                 rc = constraint_violation( cp, &b[i], op );
848                                                 if ( rc ) {
849                                                         goto add_violation;
850                                                 }
851                                         }
852                                 }
853                         if ( rc )
854                                 goto add_violation;
855
856                 }
857         }
858
859         /* Default is to just fall through to the normal processing */
860         return SLAP_CB_CONTINUE;
861
862 add_violation:
863         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
864         if (rc == LDAP_CONSTRAINT_VIOLATION ) {
865                 msg = print_message( &rsv, a->a_desc );
866         }
867         send_ldap_error(op, rs, rc, msg );
868         ch_free(msg);
869         return (rs->sr_err);
870 }
871
872
873 static int
874 constraint_check_count_violation( Modifications *m, Entry *target_entry, constraint *cp )
875 {
876         BerVarray b = NULL;
877         unsigned ce = 0;
878         unsigned ca;
879         int j;
880
881         for ( j = 0; cp->ap[j]; j++ ) {
882                 /* Get this attribute count */
883                 if ( target_entry )
884                         ce = constraint_count_attr( target_entry, cp->ap[j] );
885
886                 for( ; m; m = m->sml_next ) {
887                         if ( cp->ap[j] == m->sml_desc ) {
888                                 ca = m->sml_numvals;
889                                 switch ( m->sml_op ) {
890                                 case LDAP_MOD_DELETE:
891                                 case SLAP_MOD_SOFTDEL:
892                                         if ( !ca || ca > ce ) {
893                                                 ce = 0;
894                                         } else {
895                                                 /* No need to check for values' validity. Invalid values
896                                                  * cause the whole transaction to die anyway. */
897                                                 ce -= ca;
898                                         }
899                                         break;
900
901                                 case LDAP_MOD_ADD:
902                                 case SLAP_MOD_SOFTADD:
903                                         ce += ca;
904                                         break;
905
906                                 case LDAP_MOD_REPLACE:
907                                         ce = ca;
908                                         break;
909
910 #if 0
911                                 /* TODO */
912                                 case handle SLAP_MOD_ADD_IF_NOT_PRESENT:
913 #endif
914
915                                 default:
916                                         /* impossible! assert? */
917                                         return 1;
918                                 }
919
920                                 Debug(LDAP_DEBUG_TRACE,
921                                         "==> constraint_check_count_violation ce = %u, "
922                                         "ca = %u, cp->count = %lu\n",
923                                         ce, ca, (unsigned long) cp->count);
924                         }
925                 }
926         }
927
928         return ( ce > cp->count );
929 }
930
931 static int
932 constraint_update( Operation *op, SlapReply *rs )
933 {
934         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
935         Backend *be = op->o_bd;
936         constraint *c = on->on_bi.bi_private, *cp;
937         Entry *target_entry = NULL, *target_entry_copy = NULL;
938         Modifications *modlist, *m;
939         BerVarray b = NULL;
940         int i;
941         struct berval rsv = BER_BVC("modify breaks constraint");
942         int rc;
943         char *msg = NULL;
944         int is_v;
945
946         if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
947                 return SLAP_CB_CONTINUE;
948         }
949
950         switch ( op->o_tag ) {
951         case LDAP_REQ_MODIFY:
952                 modlist = op->orm_modlist;
953                 break;
954
955         case LDAP_REQ_MODRDN:
956                 modlist = op->orr_modlist;
957                 break;
958
959         default:
960                 /* impossible! assert? */
961                 return LDAP_OTHER;
962         }
963         
964         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0);
965         if ((m = modlist) == NULL) {
966                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
967                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
968                                                 "constraint_update() got null modlist");
969                 return(rs->sr_err);
970         }
971
972         op->o_bd = on->on_info->oi_origdb;
973         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
974         op->o_bd = be;
975
976         /* let the backend send the error */
977         if ( target_entry == NULL )
978                 return SLAP_CB_CONTINUE;
979
980         /* Do we need to count attributes? */
981         for(cp = c; cp; cp = cp->ap_next) {
982                 if (cp->type == CONSTRAINT_COUNT) {
983                         if (rc != 0 || target_entry == NULL) {
984                                 Debug(LDAP_DEBUG_TRACE, 
985                                         "==> constraint_update rc = %d DN=\"%s\"%s\n",
986                                         rc, op->o_req_ndn.bv_val,
987                                         target_entry ? "" : " not found" );
988                                 if ( rc == 0 ) 
989                                         rc = LDAP_CONSTRAINT_VIOLATION;
990                                 goto mod_violation;
991                         }
992
993                         if (cp->restrict_lud && constraint_check_restrict(op, cp, target_entry) == 0) {
994                                 continue;
995                         }
996
997                         is_v = constraint_check_count_violation(m, target_entry, cp);
998
999                         Debug(LDAP_DEBUG_TRACE,
1000                                 "==> constraint_update is_v: %d\n", is_v, 0, 0);
1001
1002                         if (is_v) {
1003                                 rc = LDAP_CONSTRAINT_VIOLATION;
1004                                 goto mod_violation;
1005                         }
1006                 }
1007         }
1008
1009         rc = LDAP_CONSTRAINT_VIOLATION;
1010         for(;m; m = m->sml_next) {
1011                 unsigned ce = 0;
1012
1013                 if (is_at_operational( m->sml_desc->ad_type )) continue;
1014
1015                 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
1016                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
1017                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
1018                         continue;
1019                 /* we only care about ADD and REPLACE modifications */
1020                 /* and DELETE are used to track attribute count */
1021                 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
1022                         continue;
1023
1024                 for(cp = c; cp; cp = cp->ap_next) {
1025                         int j;
1026                         for (j = 0; cp->ap[j]; j++) {
1027                                 if (cp->ap[j] == m->sml_desc) {
1028                                         break;
1029                                 }
1030                         }
1031                         if (cp->ap[j] == NULL) continue;
1032
1033                         if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) {
1034                                 continue;
1035                         }
1036
1037                         /* DELETE are to be ignored beyond this point */
1038                         if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
1039                                 continue;
1040
1041                         for ( i = 0; b[i].bv_val; i++ ) {
1042                                 rc = constraint_violation( cp, &b[i], op );
1043                                 if ( rc ) {
1044                                         goto mod_violation;
1045                                 }
1046                         }
1047
1048                         if (cp->type == CONSTRAINT_SET && target_entry) {
1049                                 if (target_entry_copy == NULL) {
1050                                         Modifications *ml;
1051
1052                                         target_entry_copy = entry_dup(target_entry);
1053
1054                                         /* if rename, set the new entry's name
1055                                          * (in normalized form only) */
1056                                         if ( op->o_tag == LDAP_REQ_MODRDN ) {
1057                                                 struct berval pdn, ndn = BER_BVNULL;
1058
1059                                                 if ( op->orr_nnewSup ) {
1060                                                         pdn = *op->orr_nnewSup;
1061
1062                                                 } else {
1063                                                         dnParent( &target_entry_copy->e_nname, &pdn );
1064                                                 }
1065
1066                                                 build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); 
1067
1068                                                 ber_memfree( target_entry_copy->e_nname.bv_val );
1069                                                 target_entry_copy->e_nname = ndn;
1070                                                 ber_bvreplace( &target_entry_copy->e_name, &ndn );
1071                                         }
1072
1073                                         /* apply modifications, in an attempt
1074                                          * to estimate what the entry would
1075                                          * look like in case all modifications
1076                                          * pass */
1077                                         for ( ml = modlist; ml; ml = ml->sml_next ) {
1078                                                 Modification *mod = &ml->sml_mod;
1079                                                 const char *text;
1080                                                 char textbuf[SLAP_TEXT_BUFLEN];
1081                                                 size_t textlen = sizeof(textbuf);
1082                                                 int err;
1083
1084                                                 switch ( mod->sm_op ) {
1085                                                 case LDAP_MOD_ADD:
1086                                                         err = modify_add_values( target_entry_copy,
1087                                                                 mod, get_permissiveModify(op),
1088                                                                 &text, textbuf, textlen );
1089                                                         break;
1090
1091                                                 case LDAP_MOD_DELETE:
1092                                                         err = modify_delete_values( target_entry_copy,
1093                                                                 mod, get_permissiveModify(op),
1094                                                                 &text, textbuf, textlen );
1095                                                         break;
1096
1097                                                 case LDAP_MOD_REPLACE:
1098                                                         err = modify_replace_values( target_entry_copy,
1099                                                                 mod, get_permissiveModify(op),
1100                                                                 &text, textbuf, textlen );
1101                                                         break;
1102
1103                                                 case LDAP_MOD_INCREMENT:
1104                                                         err = modify_increment_values( target_entry_copy,
1105                                                                 mod, get_permissiveModify(op),
1106                                                                 &text, textbuf, textlen );
1107                                                         break;
1108
1109                                                 case SLAP_MOD_SOFTADD:
1110                                                         mod->sm_op = LDAP_MOD_ADD;
1111                                                         err = modify_add_values( target_entry_copy,
1112                                                                 mod, get_permissiveModify(op),
1113                                                                 &text, textbuf, textlen );
1114                                                         mod->sm_op = SLAP_MOD_SOFTADD;
1115                                                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
1116                                                                 err = LDAP_SUCCESS;
1117                                                         }
1118                                                         break;
1119
1120                                                 case SLAP_MOD_SOFTDEL:
1121                                                         mod->sm_op = LDAP_MOD_ADD;
1122                                                         err = modify_delete_values( target_entry_copy,
1123                                                                 mod, get_permissiveModify(op),
1124                                                                 &text, textbuf, textlen );
1125                                                         mod->sm_op = SLAP_MOD_SOFTDEL;
1126                                                         if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
1127                                                                 err = LDAP_SUCCESS;
1128                                                         }
1129                                                         break;
1130
1131                                                 case SLAP_MOD_ADD_IF_NOT_PRESENT:
1132                                                         if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) {
1133                                                                 err = LDAP_SUCCESS;
1134                                                                 break;
1135                                                         }
1136                                                         mod->sm_op = LDAP_MOD_ADD;
1137                                                         err = modify_add_values( target_entry_copy,
1138                                                                 mod, get_permissiveModify(op),
1139                                                                 &text, textbuf, textlen );
1140                                                         mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
1141                                                         break;
1142
1143                                                 default:
1144                                                         err = LDAP_OTHER;
1145                                                         break;
1146                                                 }
1147
1148                                                 if ( err != LDAP_SUCCESS ) {
1149                                                         rc = err;
1150                                                         goto mod_violation;
1151                                                 }
1152                                         }
1153                                 }
1154
1155                                 if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
1156                                         rc = LDAP_CONSTRAINT_VIOLATION;
1157                                         goto mod_violation;
1158                                 }
1159                         }
1160                 }
1161         }
1162
1163         if (target_entry) {
1164                 op->o_bd = on->on_info->oi_origdb;
1165                 be_entry_release_r(op, target_entry);
1166                 op->o_bd = be;
1167         }
1168
1169         if (target_entry_copy) {
1170                 entry_free(target_entry_copy);
1171         }
1172
1173         return SLAP_CB_CONTINUE;
1174
1175 mod_violation:
1176         /* violation */
1177         if (target_entry) {
1178                 op->o_bd = on->on_info->oi_origdb;
1179                 be_entry_release_r(op, target_entry);
1180                 op->o_bd = be;
1181         }
1182
1183         if (target_entry_copy) {
1184                 entry_free(target_entry_copy);
1185         }
1186
1187         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
1188         if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
1189                 msg = print_message( &rsv, m->sml_desc );
1190         }
1191         send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
1192         ch_free(msg);
1193         return (rs->sr_err);
1194 }
1195
1196 static int
1197 constraint_destroy(
1198         BackendDB *be,
1199         ConfigReply *cr )
1200 {
1201         slap_overinst *on = (slap_overinst *) be->bd_info;
1202         constraint *ap, *a2;
1203
1204         for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
1205                 a2 = ap->ap_next;
1206                 constraint_free( ap, 1 );
1207         }
1208
1209         return 0;
1210 }
1211
1212 static slap_overinst constraint_ovl;
1213
1214 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
1215 static
1216 #endif
1217 int
1218 constraint_initialize( void ) {
1219         int rc;
1220
1221         constraint_ovl.on_bi.bi_type = "constraint";
1222         constraint_ovl.on_bi.bi_db_destroy = constraint_destroy;
1223         constraint_ovl.on_bi.bi_op_add = constraint_add;
1224         constraint_ovl.on_bi.bi_op_modify = constraint_update;
1225         constraint_ovl.on_bi.bi_op_modrdn = constraint_update;
1226
1227         constraint_ovl.on_bi.bi_private = NULL;
1228         
1229         constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
1230         rc = config_register_schema( constraintcfg, constraintocs );
1231         if (rc) return rc;
1232         
1233         return overlay_register( &constraint_ovl );
1234 }
1235
1236 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
1237 int init_module(int argc, char *argv[]) {
1238         return constraint_initialize();
1239 }
1240 #endif
1241
1242 #endif /* defined(SLAPD_OVER_CONSTRAINT) */
1243