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