]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/constraint.c
ITS#8605 - spelling fixes
[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  *                      Emmanuel 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 constrained 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 = { 0 };
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_response = constraint_uri_cb;
613                         cb.sc_private = &found;
614
615                         nop.o_protocol = LDAP_VERSION3;
616                         nop.o_tag = LDAP_REQ_SEARCH;
617                         nop.o_time = slap_get_time();
618                         if (c->lud->lud_dn) {
619                                 struct berval dn;
620
621                                 ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
622                                 nop.o_req_dn = dn;
623                                 nop.o_req_ndn = dn;
624                                 nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
625                                 if (!nop.o_bd) {
626                                         return LDAP_NO_SUCH_OBJECT; /* unexpected error */
627                                 }
628                                 if (!nop.o_bd->be_search) {
629                                         return LDAP_OTHER; /* unexpected error */
630                                 }
631                         } else {
632                                 nop.o_req_dn = nop.o_bd->be_nsuffix[0];
633                                 nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
634                                 nop.o_bd = on->on_info->oi_origdb;
635                         }
636                         nop.o_do_not_cache = 1;
637                         nop.o_callback = &cb;
638
639                         nop.ors_scope = c->lud->lud_scope;
640                         nop.ors_deref = LDAP_DEREF_NEVER;
641                         nop.ors_slimit = SLAP_NO_LIMIT;
642                         nop.ors_tlimit = SLAP_NO_LIMIT;
643                         nop.ors_limit = NULL;
644
645                         nop.ors_attrsonly = 0;
646                         nop.ors_attrs = slap_anlist_no_attrs;
647
648                         len = STRLENOF("(&(") +
649                                   c->filter.bv_len +
650                                   STRLENOF(")(|");
651
652                         for (i = 0; c->attrs[i]; i++) {
653                                 len += STRLENOF("(") +
654                                            c->attrs[i]->ad_cname.bv_len +
655                                            STRLENOF("=") +
656                                            bv->bv_len +
657                                            STRLENOF(")");
658                         }
659
660                         len += STRLENOF("))");
661                         filterstr.bv_len = len;
662                         filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
663
664                         ptr = filterstr.bv_val +
665                                 snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
666                         for (i = 0; c->attrs[i]; i++) {
667                                 *ptr++ = '(';
668                                 ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
669                                 *ptr++ = '=';
670                                 ptr = lutil_strcopy( ptr, bv->bv_val );
671                                 *ptr++ = ')';
672                         }
673                         *ptr++ = ')';
674                         *ptr++ = ')';
675                         *ptr++ = '\0';
676
677                         nop.ors_filterstr = filterstr;
678                         nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
679                         if ( nop.ors_filter == NULL ) {
680                                 Debug( LDAP_DEBUG_ANY,
681                                         "%s constraint_violation uri filter=\"%s\" invalid\n",
682                                         op->o_log_prefix, filterstr.bv_val, 0 );
683                                 rc = LDAP_OTHER;
684
685                         } else {
686                                 SlapReply nrs = { REP_RESULT };
687
688                                 Debug(LDAP_DEBUG_TRACE,
689                                         "==> constraint_violation uri filter = %s\n",
690                                         filterstr.bv_val, 0, 0);
691
692                                 rc = nop.o_bd->be_search( &nop, &nrs );
693
694                                 Debug(LDAP_DEBUG_TRACE,
695                                         "==> constraint_violation uri rc = %d, found = %d\n",
696                                         rc, found, 0);
697                         }
698                         op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
699
700                         if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
701                                 return rc; /* unexpected error */
702                         }
703
704                         if (!found)
705                                 return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
706                         break;
707                 }
708         }
709
710         return LDAP_SUCCESS;
711 }
712
713 static char *
714 print_message( struct berval *errtext, AttributeDescription *a )
715 {
716         char *ret;
717         int sz;
718         
719         sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
720         ret = ch_malloc(sz);
721         snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
722         return ret;
723 }
724
725 static unsigned
726 constraint_count_attr(Entry *e, AttributeDescription *ad)
727 {
728         struct Attribute *a;
729
730         if ((a = attr_find(e->e_attrs, ad)) != NULL)
731                 return a->a_numvals;
732         return 0;
733 }
734
735 static int
736 constraint_check_restrict( Operation *op, constraint *c, Entry *e )
737 {
738         assert( c->restrict_lud != NULL );
739
740         if ( c->restrict_lud->lud_dn != NULL ) {
741                 int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len;
742
743                 if ( diff < 0 ) {
744                         return 0;
745                 }
746
747                 if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) {
748                         return bvmatch( &e->e_nname, &c->restrict_ndn );
749                 }
750
751                 if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) {
752                         return 0;
753                 }
754
755                 if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) {
756                         struct berval pdn;
757
758                         if ( diff == 0 ) {
759                                 return 0;
760                         }
761
762                         dnParent( &e->e_nname, &pdn );
763
764                         if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL
765                                 && pdn.bv_len != c->restrict_ndn.bv_len )
766                         {
767                                 return 0;
768                         }
769                 }
770         }
771
772         if ( c->restrict_filter != NULL ) {
773                 int rc;
774                 struct berval save_dn = op->o_dn, save_ndn = op->o_ndn;
775
776                 op->o_dn = op->o_bd->be_rootdn;
777                 op->o_ndn = op->o_bd->be_rootndn;
778                 rc = test_filter( op, e, c->restrict_filter );
779                 op->o_dn = save_dn;
780                 op->o_ndn = save_ndn;
781
782                 if ( rc != LDAP_COMPARE_TRUE ) {
783                         return 0;
784                 }
785         }
786
787         return 1;
788 }
789
790 static int
791 constraint_add( Operation *op, SlapReply *rs )
792 {
793         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
794         Attribute *a;
795         constraint *c = on->on_bi.bi_private, *cp;
796         BerVarray b = NULL;
797         int i;
798         struct berval rsv = BER_BVC("add breaks constraint");
799         int rc = 0;
800         char *msg = NULL;
801
802         if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
803                 return SLAP_CB_CONTINUE;
804         }
805
806         if ((a = op->ora_e->e_attrs) == NULL) {
807                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
808                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
809                         "constraint_add: no attrs");
810                 return(rs->sr_err);
811         }
812
813         for(; a; a = a->a_next ) {
814                 /* we don't constrain operational attributes */
815                 if (is_at_operational(a->a_desc->ad_type)) continue;
816
817                 for(cp = c; cp; cp = cp->ap_next) {
818                         int j;
819                         for (j = 0; cp->ap[j]; j++) {
820                                 if (cp->ap[j] == a->a_desc) break;
821                         }
822                         if (cp->ap[j] == NULL) continue;
823                         if ((b = a->a_vals) == NULL) continue;
824
825                         if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) {
826                                 continue;
827                         }
828
829                         Debug(LDAP_DEBUG_TRACE, 
830                                 "==> constraint_add, "
831                                 "a->a_numvals = %u, cp->count = %lu\n",
832                                 a->a_numvals, (unsigned long) cp->count, 0);
833
834                         switch (cp->type) {
835                                 case CONSTRAINT_COUNT:
836                                         if (a->a_numvals > cp->count)
837                                                 rc = LDAP_CONSTRAINT_VIOLATION;
838                                         break;
839                                 case CONSTRAINT_SET:
840                                         if (acl_match_set(&cp->val, op, op->ora_e, NULL) == 0)
841                                                 rc = LDAP_CONSTRAINT_VIOLATION;
842                                         break;
843                                 default:
844                                         for ( i = 0; b[i].bv_val; i++ ) {
845                                                 rc = constraint_violation( cp, &b[i], op );
846                                                 if ( rc ) {
847                                                         goto add_violation;
848                                                 }
849                                         }
850                                 }
851                         if ( rc )
852                                 goto add_violation;
853
854                 }
855         }
856
857         /* Default is to just fall through to the normal processing */
858         return SLAP_CB_CONTINUE;
859
860 add_violation:
861         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
862         if (rc == LDAP_CONSTRAINT_VIOLATION ) {
863                 msg = print_message( &rsv, a->a_desc );
864         }
865         send_ldap_error(op, rs, rc, msg );
866         ch_free(msg);
867         return (rs->sr_err);
868 }
869
870
871 static int
872 constraint_check_count_violation( Modifications *m, Entry *target_entry, constraint *cp )
873 {
874         BerVarray b = NULL;
875         unsigned ce = 0;
876         unsigned ca;
877         int j;
878
879         for ( j = 0; cp->ap[j]; j++ ) {
880                 /* Get this attribute count */
881                 if ( target_entry )
882                         ce = constraint_count_attr( target_entry, cp->ap[j] );
883
884                 for( ; m; m = m->sml_next ) {
885                         if ( cp->ap[j] == m->sml_desc ) {
886                                 ca = m->sml_numvals;
887                                 switch ( m->sml_op ) {
888                                 case LDAP_MOD_DELETE:
889                                 case SLAP_MOD_SOFTDEL:
890                                         if ( !ca || ca > ce ) {
891                                                 ce = 0;
892                                         } else {
893                                                 /* No need to check for values' validity. Invalid values
894                                                  * cause the whole transaction to die anyway. */
895                                                 ce -= ca;
896                                         }
897                                         break;
898
899                                 case LDAP_MOD_ADD:
900                                 case SLAP_MOD_SOFTADD:
901                                         ce += ca;
902                                         break;
903
904                                 case LDAP_MOD_REPLACE:
905                                         ce = ca;
906                                         break;
907
908 #if 0
909                                 /* TODO */
910                                 case handle SLAP_MOD_ADD_IF_NOT_PRESENT:
911 #endif
912
913                                 default:
914                                         /* impossible! assert? */
915                                         return 1;
916                                 }
917
918                                 Debug(LDAP_DEBUG_TRACE,
919                                         "==> constraint_check_count_violation ce = %u, "
920                                         "ca = %u, cp->count = %lu\n",
921                                         ce, ca, (unsigned long) cp->count);
922                         }
923                 }
924         }
925
926         return ( ce > cp->count );
927 }
928
929 static int
930 constraint_update( Operation *op, SlapReply *rs )
931 {
932         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
933         Backend *be = op->o_bd;
934         constraint *c = on->on_bi.bi_private, *cp;
935         Entry *target_entry = NULL, *target_entry_copy = NULL;
936         Modifications *modlist, *m;
937         BerVarray b = NULL;
938         int i;
939         struct berval rsv = BER_BVC("modify breaks constraint");
940         int rc;
941         char *msg = NULL;
942         int is_v;
943
944         if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
945                 return SLAP_CB_CONTINUE;
946         }
947
948         switch ( op->o_tag ) {
949         case LDAP_REQ_MODIFY:
950                 modlist = op->orm_modlist;
951                 break;
952
953         case LDAP_REQ_MODRDN:
954                 modlist = op->orr_modlist;
955                 break;
956
957         default:
958                 /* impossible! assert? */
959                 return LDAP_OTHER;
960         }
961         
962         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0);
963         if ((m = modlist) == NULL) {
964                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
965                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
966                                                 "constraint_update() got null modlist");
967                 return(rs->sr_err);
968         }
969
970         op->o_bd = on->on_info->oi_origdb;
971         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
972         op->o_bd = be;
973
974         /* let the backend send the error */
975         if ( target_entry == NULL )
976                 return SLAP_CB_CONTINUE;
977
978         /* Do we need to count attributes? */
979         for(cp = c; cp; cp = cp->ap_next) {
980                 if (cp->type == CONSTRAINT_COUNT) {
981                         if (cp->restrict_lud && constraint_check_restrict(op, cp, target_entry) == 0) {
982                                 continue;
983                         }
984
985                         is_v = constraint_check_count_violation(m, target_entry, cp);
986
987                         Debug(LDAP_DEBUG_TRACE,
988                                 "==> constraint_update is_v: %d\n", is_v, 0, 0);
989
990                         if (is_v) {
991                                 rc = LDAP_CONSTRAINT_VIOLATION;
992                                 goto mod_violation;
993                         }
994                 }
995         }
996
997         rc = LDAP_CONSTRAINT_VIOLATION;
998         for(;m; m = m->sml_next) {
999                 unsigned ce = 0;
1000
1001                 if (is_at_operational( m->sml_desc->ad_type )) continue;
1002
1003                 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
1004                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
1005                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
1006                         continue;
1007                 /* we only care about ADD and REPLACE modifications */
1008                 /* and DELETE are used to track attribute count */
1009                 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
1010                         continue;
1011
1012                 for(cp = c; cp; cp = cp->ap_next) {
1013                         int j;
1014                         for (j = 0; cp->ap[j]; j++) {
1015                                 if (cp->ap[j] == m->sml_desc) {
1016                                         break;
1017                                 }
1018                         }
1019                         if (cp->ap[j] == NULL) continue;
1020
1021                         if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) {
1022                                 continue;
1023                         }
1024
1025                         /* DELETE are to be ignored beyond this point */
1026                         if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
1027                                 continue;
1028
1029                         for ( i = 0; b[i].bv_val; i++ ) {
1030                                 rc = constraint_violation( cp, &b[i], op );
1031                                 if ( rc ) {
1032                                         goto mod_violation;
1033                                 }
1034                         }
1035
1036                         if (cp->type == CONSTRAINT_SET && target_entry) {
1037                                 if (target_entry_copy == NULL) {
1038                                         Modifications *ml;
1039
1040                                         target_entry_copy = entry_dup(target_entry);
1041
1042                                         /* if rename, set the new entry's name
1043                                          * (in normalized form only) */
1044                                         if ( op->o_tag == LDAP_REQ_MODRDN ) {
1045                                                 struct berval pdn, ndn = BER_BVNULL;
1046
1047                                                 if ( op->orr_nnewSup ) {
1048                                                         pdn = *op->orr_nnewSup;
1049
1050                                                 } else {
1051                                                         dnParent( &target_entry_copy->e_nname, &pdn );
1052                                                 }
1053
1054                                                 build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); 
1055
1056                                                 ber_memfree( target_entry_copy->e_nname.bv_val );
1057                                                 target_entry_copy->e_nname = ndn;
1058                                                 ber_bvreplace( &target_entry_copy->e_name, &ndn );
1059                                         }
1060
1061                                         /* apply modifications, in an attempt
1062                                          * to estimate what the entry would
1063                                          * look like in case all modifications
1064                                          * pass */
1065                                         for ( ml = modlist; ml; ml = ml->sml_next ) {
1066                                                 Modification *mod = &ml->sml_mod;
1067                                                 const char *text;
1068                                                 char textbuf[SLAP_TEXT_BUFLEN];
1069                                                 size_t textlen = sizeof(textbuf);
1070                                                 int err;
1071
1072                                                 switch ( mod->sm_op ) {
1073                                                 case LDAP_MOD_ADD:
1074                                                         err = modify_add_values( target_entry_copy,
1075                                                                 mod, get_permissiveModify(op),
1076                                                                 &text, textbuf, textlen );
1077                                                         break;
1078
1079                                                 case LDAP_MOD_DELETE:
1080                                                         err = modify_delete_values( target_entry_copy,
1081                                                                 mod, get_permissiveModify(op),
1082                                                                 &text, textbuf, textlen );
1083                                                         break;
1084
1085                                                 case LDAP_MOD_REPLACE:
1086                                                         err = modify_replace_values( target_entry_copy,
1087                                                                 mod, get_permissiveModify(op),
1088                                                                 &text, textbuf, textlen );
1089                                                         break;
1090
1091                                                 case LDAP_MOD_INCREMENT:
1092                                                         err = modify_increment_values( target_entry_copy,
1093                                                                 mod, get_permissiveModify(op),
1094                                                                 &text, textbuf, textlen );
1095                                                         break;
1096
1097                                                 case SLAP_MOD_SOFTADD:
1098                                                         mod->sm_op = LDAP_MOD_ADD;
1099                                                         err = modify_add_values( target_entry_copy,
1100                                                                 mod, get_permissiveModify(op),
1101                                                                 &text, textbuf, textlen );
1102                                                         mod->sm_op = SLAP_MOD_SOFTADD;
1103                                                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
1104                                                                 err = LDAP_SUCCESS;
1105                                                         }
1106                                                         break;
1107
1108                                                 case SLAP_MOD_SOFTDEL:
1109                                                         mod->sm_op = LDAP_MOD_ADD;
1110                                                         err = modify_delete_values( target_entry_copy,
1111                                                                 mod, get_permissiveModify(op),
1112                                                                 &text, textbuf, textlen );
1113                                                         mod->sm_op = SLAP_MOD_SOFTDEL;
1114                                                         if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
1115                                                                 err = LDAP_SUCCESS;
1116                                                         }
1117                                                         break;
1118
1119                                                 case SLAP_MOD_ADD_IF_NOT_PRESENT:
1120                                                         if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) {
1121                                                                 err = LDAP_SUCCESS;
1122                                                                 break;
1123                                                         }
1124                                                         mod->sm_op = LDAP_MOD_ADD;
1125                                                         err = modify_add_values( target_entry_copy,
1126                                                                 mod, get_permissiveModify(op),
1127                                                                 &text, textbuf, textlen );
1128                                                         mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
1129                                                         break;
1130
1131                                                 default:
1132                                                         err = LDAP_OTHER;
1133                                                         break;
1134                                                 }
1135
1136                                                 if ( err != LDAP_SUCCESS ) {
1137                                                         rc = err;
1138                                                         goto mod_violation;
1139                                                 }
1140                                         }
1141                                 }
1142
1143                                 if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
1144                                         rc = LDAP_CONSTRAINT_VIOLATION;
1145                                         goto mod_violation;
1146                                 }
1147                         }
1148                 }
1149         }
1150
1151         if (target_entry) {
1152                 op->o_bd = on->on_info->oi_origdb;
1153                 be_entry_release_r(op, target_entry);
1154                 op->o_bd = be;
1155         }
1156
1157         if (target_entry_copy) {
1158                 entry_free(target_entry_copy);
1159         }
1160
1161         return SLAP_CB_CONTINUE;
1162
1163 mod_violation:
1164         /* violation */
1165         if (target_entry) {
1166                 op->o_bd = on->on_info->oi_origdb;
1167                 be_entry_release_r(op, target_entry);
1168                 op->o_bd = be;
1169         }
1170
1171         if (target_entry_copy) {
1172                 entry_free(target_entry_copy);
1173         }
1174
1175         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
1176         if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
1177                 msg = print_message( &rsv, m->sml_desc );
1178         }
1179         send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
1180         ch_free(msg);
1181         return (rs->sr_err);
1182 }
1183
1184 static int
1185 constraint_destroy(
1186         BackendDB *be,
1187         ConfigReply *cr )
1188 {
1189         slap_overinst *on = (slap_overinst *) be->bd_info;
1190         constraint *ap, *a2;
1191
1192         for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
1193                 a2 = ap->ap_next;
1194                 constraint_free( ap, 1 );
1195         }
1196
1197         return 0;
1198 }
1199
1200 static slap_overinst constraint_ovl;
1201
1202 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
1203 static
1204 #endif
1205 int
1206 constraint_initialize( void ) {
1207         int rc;
1208
1209         constraint_ovl.on_bi.bi_type = "constraint";
1210         constraint_ovl.on_bi.bi_db_destroy = constraint_destroy;
1211         constraint_ovl.on_bi.bi_op_add = constraint_add;
1212         constraint_ovl.on_bi.bi_op_modify = constraint_update;
1213         constraint_ovl.on_bi.bi_op_modrdn = constraint_update;
1214
1215         constraint_ovl.on_bi.bi_private = NULL;
1216         
1217         constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
1218         rc = config_register_schema( constraintcfg, constraintocs );
1219         if (rc) return rc;
1220         
1221         return overlay_register( &constraint_ovl );
1222 }
1223
1224 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
1225 int init_module(int argc, char *argv[]) {
1226         return constraint_initialize();
1227 }
1228 #endif
1229
1230 #endif /* defined(SLAPD_OVER_CONSTRAINT) */
1231