1 1.3 christos /* $NetBSD: limits.c,v 1.4 2025/09/05 21:16:25 christos Exp $ */ 2 1.2 christos 3 1.1 lukem /* limits.c - routines to handle regex-based size and time limits */ 4 1.2 christos /* $OpenLDAP$ */ 5 1.1 lukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 lukem * 7 1.4 christos * Copyright 1998-2024 The OpenLDAP Foundation. 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.2 christos #include <sys/cdefs.h> 20 1.3 christos __RCSID("$NetBSD: limits.c,v 1.4 2025/09/05 21:16:25 christos Exp $"); 21 1.2 christos 22 1.1 lukem #include "portable.h" 23 1.1 lukem 24 1.1 lukem #include <stdio.h> 25 1.1 lukem 26 1.2 christos #include <ac/ctype.h> 27 1.1 lukem #include <ac/regex.h> 28 1.1 lukem #include <ac/string.h> 29 1.1 lukem 30 1.1 lukem #include "slap.h" 31 1.1 lukem #include "lutil.h" 32 1.1 lukem 33 1.1 lukem /* define to get an error if requesting limit higher than hard */ 34 1.1 lukem #undef ABOVE_HARD_LIMIT_IS_ERROR 35 1.1 lukem 36 1.2 christos static const struct berval lmpats[] = { 37 1.2 christos BER_BVC( "base" ), 38 1.2 christos BER_BVC( "base" ), 39 1.2 christos BER_BVC( "onelevel" ), 40 1.2 christos BER_BVC( "subtree" ), 41 1.2 christos BER_BVC( "children" ), 42 1.2 christos BER_BVC( "regex" ), 43 1.2 christos BER_BVC( "anonymous" ), 44 1.2 christos BER_BVC( "users" ), 45 1.2 christos BER_BVC( "*" ) 46 1.2 christos }; 47 1.2 christos 48 1.2 christos #ifdef LDAP_DEBUG 49 1.2 christos static const char *const dn_source[2] = { "DN", "DN.THIS" }; 50 1.2 christos static const char *const lmpats_out[] = { 51 1.2 christos "UNDEFINED", 52 1.2 christos "EXACT", 53 1.2 christos "ONELEVEL", 54 1.2 christos "SUBTREE", 55 1.2 christos "CHILDREN", 56 1.2 christos "REGEX", 57 1.2 christos "ANONYMOUS", 58 1.2 christos "USERS", 59 1.2 christos "ANY" 60 1.2 christos }; 61 1.2 christos 62 1.2 christos static const char * 63 1.1 lukem limits2str( unsigned i ) 64 1.1 lukem { 65 1.2 christos return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] )) 66 1.2 christos ? lmpats_out[i] : "UNKNOWN"; 67 1.1 lukem } 68 1.2 christos #endif /* LDAP_DEBUG */ 69 1.1 lukem 70 1.2 christos static int 71 1.1 lukem limits_get( 72 1.1 lukem Operation *op, 73 1.1 lukem struct slap_limits_set **limit 74 1.1 lukem ) 75 1.1 lukem { 76 1.2 christos static struct berval empty_dn = BER_BVC( "" ); 77 1.1 lukem struct slap_limits **lm; 78 1.2 christos struct berval *ndns[2]; 79 1.1 lukem 80 1.1 lukem assert( op != NULL ); 81 1.1 lukem assert( limit != NULL ); 82 1.1 lukem 83 1.2 christos ndns[0] = &op->o_ndn; 84 1.2 christos ndns[1] = &op->o_req_ndn; 85 1.2 christos 86 1.2 christos Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n", 87 1.1 lukem op->o_log_prefix, 88 1.2 christos BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val, 89 1.2 christos BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val ); 90 1.1 lukem /* 91 1.1 lukem * default values 92 1.1 lukem */ 93 1.1 lukem *limit = &op->o_bd->be_def_limit; 94 1.1 lukem 95 1.1 lukem if ( op->o_bd->be_limits == NULL ) { 96 1.1 lukem return( 0 ); 97 1.1 lukem } 98 1.1 lukem 99 1.1 lukem for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) { 100 1.1 lukem unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK; 101 1.1 lukem unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK; 102 1.2 christos unsigned isthis = type == SLAP_LIMITS_TYPE_THIS; 103 1.2 christos struct berval *ndn = ndns[isthis]; 104 1.2 christos 105 1.2 christos if ( style == SLAP_LIMITS_ANY ) 106 1.2 christos goto found_any; 107 1.2 christos 108 1.2 christos if ( BER_BVISEMPTY( ndn ) ) { 109 1.2 christos if ( style == SLAP_LIMITS_ANONYMOUS ) 110 1.2 christos goto found_nodn; 111 1.2 christos if ( !isthis ) 112 1.2 christos continue; 113 1.2 christos ndn = &empty_dn; 114 1.2 christos } 115 1.1 lukem 116 1.1 lukem switch ( style ) { 117 1.1 lukem case SLAP_LIMITS_EXACT: 118 1.1 lukem if ( type == SLAP_LIMITS_TYPE_GROUP ) { 119 1.2 christos int rc = backend_group( op, NULL, 120 1.1 lukem &lm[0]->lm_pat, ndn, 121 1.1 lukem lm[0]->lm_group_oc, 122 1.1 lukem lm[0]->lm_group_ad ); 123 1.1 lukem if ( rc == 0 ) { 124 1.2 christos goto found_group; 125 1.1 lukem } 126 1.1 lukem } else { 127 1.1 lukem if ( dn_match( &lm[0]->lm_pat, ndn ) ) { 128 1.2 christos goto found_dn; 129 1.1 lukem } 130 1.1 lukem } 131 1.1 lukem break; 132 1.1 lukem 133 1.1 lukem case SLAP_LIMITS_ONE: 134 1.1 lukem case SLAP_LIMITS_SUBTREE: 135 1.1 lukem case SLAP_LIMITS_CHILDREN: { 136 1.2 christos ber_len_t d; 137 1.1 lukem 138 1.2 christos /* ndn shorter than lm_pat */ 139 1.1 lukem if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) { 140 1.1 lukem break; 141 1.1 lukem } 142 1.1 lukem d = ndn->bv_len - lm[0]->lm_pat.bv_len; 143 1.1 lukem 144 1.1 lukem if ( d == 0 ) { 145 1.2 christos /* allow exact match for SUBTREE only */ 146 1.1 lukem if ( style != SLAP_LIMITS_SUBTREE ) { 147 1.1 lukem break; 148 1.1 lukem } 149 1.1 lukem } else { 150 1.1 lukem /* check for unescaped rdn separator */ 151 1.1 lukem if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) { 152 1.1 lukem break; 153 1.1 lukem } 154 1.1 lukem } 155 1.1 lukem 156 1.2 christos /* check that ndn ends with lm_pat */ 157 1.2 christos if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) { 158 1.2 christos break; 159 1.2 christos } 160 1.2 christos 161 1.2 christos /* in case of ONE, require exactly one rdn below lm_pat */ 162 1.2 christos if ( style == SLAP_LIMITS_ONE ) { 163 1.2 christos if ( dn_rdnlen( NULL, ndn ) != d - 1 ) { 164 1.2 christos break; 165 1.1 lukem } 166 1.1 lukem } 167 1.1 lukem 168 1.2 christos goto found_dn; 169 1.1 lukem } 170 1.1 lukem 171 1.1 lukem case SLAP_LIMITS_REGEX: 172 1.2 christos if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) { 173 1.2 christos goto found_dn; 174 1.1 lukem } 175 1.1 lukem break; 176 1.1 lukem 177 1.1 lukem case SLAP_LIMITS_ANONYMOUS: 178 1.1 lukem break; 179 1.1 lukem 180 1.1 lukem case SLAP_LIMITS_USERS: 181 1.2 christos found_nodn: 182 1.2 christos Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n", 183 1.3 christos dn_source[isthis], limits2str( style ) ); 184 1.2 christos found_any: 185 1.2 christos *limit = &lm[0]->lm_limits; 186 1.2 christos return( 0 ); 187 1.2 christos 188 1.2 christos found_dn: 189 1.2 christos Debug( LDAP_DEBUG_TRACE, 190 1.2 christos "<== limits_get: type=%s match=%s dn=\"%s\"\n", 191 1.2 christos dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val ); 192 1.2 christos *limit = &lm[0]->lm_limits; 193 1.2 christos return( 0 ); 194 1.1 lukem 195 1.2 christos found_group: 196 1.2 christos Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT " 197 1.2 christos "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n", 198 1.2 christos lm[0]->lm_pat.bv_val, 199 1.2 christos lm[0]->lm_group_oc->soc_cname.bv_val, 200 1.2 christos lm[0]->lm_group_ad->ad_cname.bv_val ); 201 1.1 lukem *limit = &lm[0]->lm_limits; 202 1.1 lukem return( 0 ); 203 1.1 lukem 204 1.1 lukem default: 205 1.1 lukem assert( 0 ); /* unreachable */ 206 1.1 lukem return( -1 ); 207 1.1 lukem } 208 1.1 lukem } 209 1.1 lukem 210 1.1 lukem return( 0 ); 211 1.1 lukem } 212 1.1 lukem 213 1.1 lukem static int 214 1.1 lukem limits_add( 215 1.1 lukem Backend *be, 216 1.1 lukem unsigned flags, 217 1.1 lukem const char *pattern, 218 1.1 lukem ObjectClass *group_oc, 219 1.1 lukem AttributeDescription *group_ad, 220 1.1 lukem struct slap_limits_set *limit 221 1.1 lukem ) 222 1.1 lukem { 223 1.1 lukem int i; 224 1.1 lukem struct slap_limits *lm; 225 1.1 lukem unsigned type, style; 226 1.1 lukem 227 1.1 lukem assert( be != NULL ); 228 1.1 lukem assert( limit != NULL ); 229 1.1 lukem 230 1.1 lukem type = flags & SLAP_LIMITS_TYPE_MASK; 231 1.1 lukem style = flags & SLAP_LIMITS_MASK; 232 1.1 lukem 233 1.1 lukem switch ( style ) { 234 1.1 lukem case SLAP_LIMITS_ANONYMOUS: 235 1.1 lukem case SLAP_LIMITS_USERS: 236 1.1 lukem case SLAP_LIMITS_ANY: 237 1.2 christos /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */ 238 1.1 lukem for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) { 239 1.1 lukem if ( be->be_limits[ i ]->lm_flags == style ) { 240 1.1 lukem return( -1 ); 241 1.1 lukem } 242 1.1 lukem } 243 1.1 lukem break; 244 1.1 lukem } 245 1.1 lukem 246 1.1 lukem 247 1.1 lukem lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 ); 248 1.1 lukem 249 1.1 lukem switch ( style ) { 250 1.1 lukem case SLAP_LIMITS_UNDEFINED: 251 1.1 lukem style = SLAP_LIMITS_EXACT; 252 1.1 lukem /* continue to next cases */ 253 1.1 lukem case SLAP_LIMITS_EXACT: 254 1.1 lukem case SLAP_LIMITS_ONE: 255 1.1 lukem case SLAP_LIMITS_SUBTREE: 256 1.1 lukem case SLAP_LIMITS_CHILDREN: 257 1.1 lukem { 258 1.1 lukem int rc; 259 1.1 lukem struct berval bv; 260 1.1 lukem 261 1.1 lukem ber_str2bv( pattern, 0, 0, &bv ); 262 1.1 lukem 263 1.1 lukem rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL ); 264 1.1 lukem if ( rc != LDAP_SUCCESS ) { 265 1.1 lukem ch_free( lm ); 266 1.1 lukem return( -1 ); 267 1.1 lukem } 268 1.1 lukem } 269 1.1 lukem break; 270 1.1 lukem 271 1.1 lukem case SLAP_LIMITS_REGEX: 272 1.1 lukem ber_str2bv( pattern, 0, 1, &lm->lm_pat ); 273 1.1 lukem if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 274 1.1 lukem REG_EXTENDED | REG_ICASE ) ) { 275 1.1 lukem free( lm->lm_pat.bv_val ); 276 1.1 lukem ch_free( lm ); 277 1.1 lukem return( -1 ); 278 1.1 lukem } 279 1.1 lukem break; 280 1.1 lukem 281 1.1 lukem case SLAP_LIMITS_ANONYMOUS: 282 1.1 lukem case SLAP_LIMITS_USERS: 283 1.1 lukem case SLAP_LIMITS_ANY: 284 1.1 lukem BER_BVZERO( &lm->lm_pat ); 285 1.1 lukem break; 286 1.1 lukem } 287 1.1 lukem 288 1.1 lukem switch ( type ) { 289 1.1 lukem case SLAP_LIMITS_TYPE_GROUP: 290 1.1 lukem assert( group_oc != NULL ); 291 1.1 lukem assert( group_ad != NULL ); 292 1.1 lukem lm->lm_group_oc = group_oc; 293 1.1 lukem lm->lm_group_ad = group_ad; 294 1.1 lukem break; 295 1.1 lukem } 296 1.1 lukem 297 1.2 christos lm->lm_flags = style | type; 298 1.1 lukem lm->lm_limits = *limit; 299 1.1 lukem 300 1.1 lukem i = 0; 301 1.1 lukem if ( be->be_limits != NULL ) { 302 1.1 lukem for ( ; be->be_limits[i]; i++ ); 303 1.1 lukem } 304 1.1 lukem 305 1.1 lukem be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits, 306 1.1 lukem sizeof( struct slap_limits * ) * ( i + 2 ) ); 307 1.1 lukem be->be_limits[i] = lm; 308 1.1 lukem be->be_limits[i+1] = NULL; 309 1.1 lukem 310 1.1 lukem return( 0 ); 311 1.1 lukem } 312 1.1 lukem 313 1.2 christos #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0) 314 1.2 christos 315 1.1 lukem int 316 1.1 lukem limits_parse( 317 1.1 lukem Backend *be, 318 1.1 lukem const char *fname, 319 1.1 lukem int lineno, 320 1.1 lukem int argc, 321 1.1 lukem char **argv 322 1.1 lukem ) 323 1.1 lukem { 324 1.1 lukem int flags = SLAP_LIMITS_UNDEFINED; 325 1.1 lukem char *pattern; 326 1.1 lukem struct slap_limits_set limit; 327 1.1 lukem int i, rc = 0; 328 1.1 lukem ObjectClass *group_oc = NULL; 329 1.1 lukem AttributeDescription *group_ad = NULL; 330 1.1 lukem 331 1.1 lukem assert( be != NULL ); 332 1.1 lukem 333 1.1 lukem if ( argc < 3 ) { 334 1.1 lukem Debug( LDAP_DEBUG_ANY, 335 1.1 lukem "%s : line %d: missing arg(s) in " 336 1.3 christos "\"limits <pattern> <limits>\" line.\n", 337 1.3 christos fname, lineno ); 338 1.1 lukem return( -1 ); 339 1.1 lukem } 340 1.1 lukem 341 1.1 lukem limit = be->be_def_limit; 342 1.1 lukem 343 1.1 lukem /* 344 1.1 lukem * syntax: 345 1.1 lukem * 346 1.1 lukem * "limits" <pattern> <limit> [ ... ] 347 1.1 lukem * 348 1.1 lukem * 349 1.1 lukem * <pattern>: 350 1.1 lukem * 351 1.1 lukem * "anonymous" 352 1.1 lukem * "users" 353 1.2 christos * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" | 354 1.2 christos * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ] 355 1.2 christos * "=" ] <dn pattern> 356 1.1 lukem * 357 1.1 lukem * Note: 358 1.2 christos * "this" is the baseobject, "self" (the default) is the bound DN 359 1.1 lukem * "exact" and "base" are the same (exact match); 360 1.1 lukem * "onelevel" means exactly one rdn below, NOT including pattern 361 1.1 lukem * "subtree" means any rdn below, including pattern 362 1.1 lukem * "children" means any rdn below, NOT including pattern 363 1.1 lukem * 364 1.1 lukem * "anonymous" may be deprecated in favour 365 1.1 lukem * of the pattern = "anonymous" form 366 1.1 lukem * 367 1.1 lukem * "group[/objectClass[/attributeType]]" "=" "<dn pattern>" 368 1.1 lukem * 369 1.1 lukem * <limit>: 370 1.1 lukem * 371 1.1 lukem * "time" [ "." { "soft" | "hard" } ] "=" <integer> 372 1.1 lukem * 373 1.1 lukem * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer> 374 1.1 lukem */ 375 1.1 lukem 376 1.1 lukem pattern = argv[1]; 377 1.1 lukem if ( strcmp( pattern, "*" ) == 0) { 378 1.1 lukem flags = SLAP_LIMITS_ANY; 379 1.1 lukem 380 1.1 lukem } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) { 381 1.1 lukem flags = SLAP_LIMITS_ANONYMOUS; 382 1.1 lukem 383 1.1 lukem } else if ( strcasecmp( pattern, "users" ) == 0 ) { 384 1.1 lukem flags = SLAP_LIMITS_USERS; 385 1.1 lukem 386 1.2 christos } else if ( STRSTART( pattern, "dn" ) ) { 387 1.1 lukem pattern += STRLENOF( "dn" ); 388 1.2 christos flags = SLAP_LIMITS_TYPE_SELF; 389 1.1 lukem if ( pattern[0] == '.' ) { 390 1.1 lukem pattern++; 391 1.2 christos if ( STRSTART( pattern, "this" ) ) { 392 1.2 christos flags = SLAP_LIMITS_TYPE_THIS; 393 1.2 christos pattern += STRLENOF( "this" ); 394 1.2 christos } else if ( STRSTART( pattern, "self" ) ) { 395 1.2 christos pattern += STRLENOF( "self" ); 396 1.2 christos } else { 397 1.2 christos goto got_dn_dot; 398 1.2 christos } 399 1.2 christos } 400 1.2 christos if ( pattern[0] == '.' ) { 401 1.2 christos pattern++; 402 1.2 christos got_dn_dot: 403 1.2 christos if ( STRSTART( pattern, "exact" ) ) { 404 1.2 christos flags |= SLAP_LIMITS_EXACT; 405 1.1 lukem pattern += STRLENOF( "exact" ); 406 1.1 lukem 407 1.2 christos } else if ( STRSTART( pattern, "base" ) ) { 408 1.2 christos flags |= SLAP_LIMITS_BASE; 409 1.1 lukem pattern += STRLENOF( "base" ); 410 1.1 lukem 411 1.2 christos } else if ( STRSTART( pattern, "one" ) ) { 412 1.2 christos flags |= SLAP_LIMITS_ONE; 413 1.1 lukem pattern += STRLENOF( "one" ); 414 1.2 christos if ( STRSTART( pattern, "level" ) ) { 415 1.1 lukem pattern += STRLENOF( "level" ); 416 1.1 lukem 417 1.1 lukem } else { 418 1.1 lukem Debug( LDAP_DEBUG_ANY, 419 1.1 lukem "%s : line %d: deprecated \"one\" style " 420 1.1 lukem "\"limits <pattern> <limits>\" line; " 421 1.3 christos "use \"onelevel\" instead.\n", fname, lineno ); 422 1.1 lukem } 423 1.1 lukem 424 1.2 christos } else if ( STRSTART( pattern, "sub" ) ) { 425 1.2 christos flags |= SLAP_LIMITS_SUBTREE; 426 1.1 lukem pattern += STRLENOF( "sub" ); 427 1.2 christos if ( STRSTART( pattern, "tree" ) ) { 428 1.1 lukem pattern += STRLENOF( "tree" ); 429 1.1 lukem 430 1.1 lukem } else { 431 1.1 lukem Debug( LDAP_DEBUG_ANY, 432 1.1 lukem "%s : line %d: deprecated \"sub\" style " 433 1.1 lukem "\"limits <pattern> <limits>\" line; " 434 1.3 christos "use \"subtree\" instead.\n", fname, lineno ); 435 1.1 lukem } 436 1.1 lukem 437 1.2 christos } else if ( STRSTART( pattern, "children" ) ) { 438 1.2 christos flags |= SLAP_LIMITS_CHILDREN; 439 1.1 lukem pattern += STRLENOF( "children" ); 440 1.1 lukem 441 1.2 christos } else if ( STRSTART( pattern, "regex" ) ) { 442 1.2 christos flags |= SLAP_LIMITS_REGEX; 443 1.1 lukem pattern += STRLENOF( "regex" ); 444 1.1 lukem 445 1.1 lukem /* 446 1.1 lukem * this could be deprecated in favour 447 1.1 lukem * of the pattern = "anonymous" form 448 1.1 lukem */ 449 1.2 christos } else if ( STRSTART( pattern, "anonymous" ) 450 1.2 christos && flags == SLAP_LIMITS_TYPE_SELF ) 451 1.2 christos { 452 1.1 lukem flags = SLAP_LIMITS_ANONYMOUS; 453 1.1 lukem pattern = NULL; 454 1.2 christos 455 1.2 christos } else { 456 1.2 christos /* force error below */ 457 1.2 christos if ( *pattern == '=' ) 458 1.2 christos --pattern; 459 1.1 lukem } 460 1.1 lukem } 461 1.1 lukem 462 1.1 lukem /* pre-check the data */ 463 1.2 christos if ( pattern != NULL ) { 464 1.1 lukem if ( pattern[0] != '=' ) { 465 1.1 lukem Debug( LDAP_DEBUG_ANY, 466 1.2 christos "%s : line %d: %s in " 467 1.2 christos "\"dn[.{this|self}][.{exact|base" 468 1.2 christos "|onelevel|subtree|children|regex" 469 1.2 christos "|anonymous}]=<pattern>\" in " 470 1.2 christos "\"limits <pattern> <limits>\" line.\n", 471 1.2 christos fname, lineno, 472 1.2 christos isalnum( (unsigned char)pattern[0] ) 473 1.2 christos ? "unknown DN modifier" : "missing '='" ); 474 1.1 lukem return( -1 ); 475 1.1 lukem } 476 1.1 lukem 477 1.1 lukem /* skip '=' (required) */ 478 1.1 lukem pattern++; 479 1.1 lukem 480 1.1 lukem /* trim obvious cases */ 481 1.1 lukem if ( strcmp( pattern, "*" ) == 0 ) { 482 1.1 lukem flags = SLAP_LIMITS_ANY; 483 1.1 lukem pattern = NULL; 484 1.1 lukem 485 1.2 christos } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX 486 1.1 lukem && strcmp( pattern, ".*" ) == 0 ) { 487 1.1 lukem flags = SLAP_LIMITS_ANY; 488 1.1 lukem pattern = NULL; 489 1.1 lukem } 490 1.1 lukem } 491 1.1 lukem 492 1.2 christos } else if (STRSTART( pattern, "group" ) ) { 493 1.1 lukem pattern += STRLENOF( "group" ); 494 1.1 lukem 495 1.1 lukem if ( pattern[0] == '/' ) { 496 1.1 lukem struct berval oc, ad; 497 1.1 lukem 498 1.1 lukem oc.bv_val = pattern + 1; 499 1.1 lukem pattern = strchr( pattern, '=' ); 500 1.1 lukem if ( pattern == NULL ) { 501 1.1 lukem return -1; 502 1.1 lukem } 503 1.1 lukem 504 1.1 lukem ad.bv_val = strchr( oc.bv_val, '/' ); 505 1.1 lukem if ( ad.bv_val != NULL ) { 506 1.1 lukem const char *text = NULL; 507 1.1 lukem 508 1.1 lukem oc.bv_len = ad.bv_val - oc.bv_val; 509 1.1 lukem 510 1.1 lukem ad.bv_val++; 511 1.1 lukem ad.bv_len = pattern - ad.bv_val; 512 1.1 lukem rc = slap_bv2ad( &ad, &group_ad, &text ); 513 1.1 lukem if ( rc != LDAP_SUCCESS ) { 514 1.1 lukem goto no_ad; 515 1.1 lukem } 516 1.1 lukem 517 1.1 lukem } else { 518 1.1 lukem oc.bv_len = pattern - oc.bv_val; 519 1.1 lukem } 520 1.1 lukem 521 1.1 lukem group_oc = oc_bvfind( &oc ); 522 1.1 lukem if ( group_oc == NULL ) { 523 1.1 lukem goto no_oc; 524 1.1 lukem } 525 1.1 lukem } 526 1.1 lukem 527 1.1 lukem if ( group_oc == NULL ) { 528 1.1 lukem group_oc = oc_find( SLAPD_GROUP_CLASS ); 529 1.1 lukem if ( group_oc == NULL ) { 530 1.1 lukem no_oc:; 531 1.1 lukem return( -1 ); 532 1.1 lukem } 533 1.1 lukem } 534 1.1 lukem 535 1.1 lukem if ( group_ad == NULL ) { 536 1.1 lukem const char *text = NULL; 537 1.1 lukem 538 1.1 lukem rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text ); 539 1.1 lukem 540 1.1 lukem if ( rc != LDAP_SUCCESS ) { 541 1.1 lukem no_ad:; 542 1.1 lukem return( -1 ); 543 1.1 lukem } 544 1.1 lukem } 545 1.1 lukem 546 1.1 lukem flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT; 547 1.1 lukem 548 1.1 lukem if ( pattern[0] != '=' ) { 549 1.1 lukem Debug( LDAP_DEBUG_ANY, 550 1.1 lukem "%s : line %d: missing '=' in " 551 1.1 lukem "\"group[/objectClass[/attributeType]]" 552 1.1 lukem "=<pattern>\" in " 553 1.1 lukem "\"limits <pattern> <limits>\" line.\n", 554 1.3 christos fname, lineno ); 555 1.1 lukem return( -1 ); 556 1.1 lukem } 557 1.1 lukem 558 1.1 lukem /* skip '=' (required) */ 559 1.1 lukem pattern++; 560 1.1 lukem } 561 1.1 lukem 562 1.1 lukem /* get the limits */ 563 1.1 lukem for ( i = 2; i < argc; i++ ) { 564 1.1 lukem if ( limits_parse_one( argv[i], &limit ) ) { 565 1.1 lukem 566 1.1 lukem Debug( LDAP_DEBUG_ANY, 567 1.1 lukem "%s : line %d: unknown limit values \"%s\" in " 568 1.1 lukem "\"limits <pattern> <limits>\" line.\n", 569 1.1 lukem fname, lineno, argv[i] ); 570 1.1 lukem 571 1.1 lukem return( 1 ); 572 1.1 lukem } 573 1.1 lukem } 574 1.1 lukem 575 1.1 lukem /* 576 1.1 lukem * sanity checks ... 577 1.1 lukem * 578 1.1 lukem * FIXME: add warnings? 579 1.1 lukem */ 580 1.1 lukem if ( limit.lms_t_hard > 0 && 581 1.1 lukem ( limit.lms_t_hard < limit.lms_t_soft 582 1.1 lukem || limit.lms_t_soft == -1 ) ) { 583 1.1 lukem limit.lms_t_hard = limit.lms_t_soft; 584 1.1 lukem } 585 1.1 lukem 586 1.1 lukem if ( limit.lms_s_hard > 0 && 587 1.1 lukem ( limit.lms_s_hard < limit.lms_s_soft 588 1.1 lukem || limit.lms_s_soft == -1 ) ) { 589 1.1 lukem limit.lms_s_hard = limit.lms_s_soft; 590 1.1 lukem } 591 1.1 lukem 592 1.1 lukem /* 593 1.1 lukem * defaults ... 594 1.1 lukem * 595 1.1 lukem * lms_t_hard: 596 1.1 lukem * -1 => no limits 597 1.1 lukem * 0 => same as soft 598 1.1 lukem * > 0 => limit (in seconds) 599 1.1 lukem * 600 1.1 lukem * lms_s_hard: 601 1.1 lukem * -1 => no limits 602 1.1 lukem * 0 0> same as soft 603 1.1 lukem * > 0 => limit (in entries) 604 1.1 lukem * 605 1.1 lukem * lms_s_pr_total: 606 1.1 lukem * -2 => disable the control 607 1.1 lukem * -1 => no limits 608 1.1 lukem * 0 => same as soft 609 1.1 lukem * > 0 => limit (in entries) 610 1.1 lukem * 611 1.1 lukem * lms_s_pr: 612 1.1 lukem * -1 => no limits 613 1.1 lukem * 0 => no limits? 614 1.1 lukem * > 0 => limit size (in entries) 615 1.1 lukem */ 616 1.1 lukem if ( limit.lms_s_pr_total > 0 && 617 1.1 lukem limit.lms_s_pr > limit.lms_s_pr_total ) { 618 1.1 lukem limit.lms_s_pr = limit.lms_s_pr_total; 619 1.1 lukem } 620 1.1 lukem 621 1.1 lukem rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit ); 622 1.1 lukem if ( rc ) { 623 1.1 lukem 624 1.1 lukem Debug( LDAP_DEBUG_ANY, 625 1.1 lukem "%s : line %d: unable to add limit in " 626 1.1 lukem "\"limits <pattern> <limits>\" line.\n", 627 1.3 christos fname, lineno ); 628 1.1 lukem } 629 1.1 lukem 630 1.1 lukem return( rc ); 631 1.1 lukem } 632 1.1 lukem 633 1.1 lukem int 634 1.1 lukem limits_parse_one( 635 1.1 lukem const char *arg, 636 1.1 lukem struct slap_limits_set *limit 637 1.1 lukem ) 638 1.1 lukem { 639 1.1 lukem assert( arg != NULL ); 640 1.1 lukem assert( limit != NULL ); 641 1.1 lukem 642 1.2 christos if ( STRSTART( arg, "time" ) ) { 643 1.1 lukem arg += STRLENOF( "time" ); 644 1.1 lukem 645 1.1 lukem if ( arg[0] == '.' ) { 646 1.1 lukem arg++; 647 1.2 christos if ( STRSTART( arg, "soft=" ) ) { 648 1.1 lukem arg += STRLENOF( "soft=" ); 649 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 650 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 651 1.2 christos { 652 1.1 lukem limit->lms_t_soft = -1; 653 1.1 lukem 654 1.1 lukem } else { 655 1.1 lukem int soft; 656 1.1 lukem 657 1.1 lukem if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) { 658 1.1 lukem return( 1 ); 659 1.1 lukem } 660 1.1 lukem 661 1.1 lukem if ( soft == -1 ) { 662 1.1 lukem /* FIXME: use "unlimited" instead; issue warning? */ 663 1.1 lukem } 664 1.1 lukem 665 1.1 lukem limit->lms_t_soft = soft; 666 1.1 lukem } 667 1.1 lukem 668 1.2 christos } else if ( STRSTART( arg, "hard=" ) ) { 669 1.1 lukem arg += STRLENOF( "hard=" ); 670 1.1 lukem if ( strcasecmp( arg, "soft" ) == 0 ) { 671 1.1 lukem limit->lms_t_hard = 0; 672 1.1 lukem 673 1.2 christos } else if ( strcasecmp( arg, "unlimited" ) == 0 674 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 675 1.2 christos { 676 1.1 lukem limit->lms_t_hard = -1; 677 1.1 lukem 678 1.1 lukem } else { 679 1.1 lukem int hard; 680 1.1 lukem 681 1.1 lukem if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) { 682 1.1 lukem return( 1 ); 683 1.1 lukem } 684 1.1 lukem 685 1.1 lukem if ( hard == -1 ) { 686 1.1 lukem /* FIXME: use "unlimited" instead */ 687 1.1 lukem } 688 1.1 lukem 689 1.1 lukem if ( hard == 0 ) { 690 1.1 lukem /* FIXME: use "soft" instead */ 691 1.1 lukem } 692 1.1 lukem 693 1.1 lukem limit->lms_t_hard = hard; 694 1.1 lukem } 695 1.1 lukem 696 1.1 lukem } else { 697 1.1 lukem return( 1 ); 698 1.1 lukem } 699 1.1 lukem 700 1.1 lukem } else if ( arg[0] == '=' ) { 701 1.1 lukem arg++; 702 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 703 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 704 1.2 christos { 705 1.1 lukem limit->lms_t_soft = -1; 706 1.1 lukem 707 1.1 lukem } else { 708 1.1 lukem if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0 709 1.1 lukem || limit->lms_t_soft < -1 ) 710 1.1 lukem { 711 1.1 lukem return( 1 ); 712 1.1 lukem } 713 1.1 lukem } 714 1.1 lukem limit->lms_t_hard = 0; 715 1.1 lukem 716 1.1 lukem } else { 717 1.1 lukem return( 1 ); 718 1.1 lukem } 719 1.1 lukem 720 1.2 christos } else if ( STRSTART( arg, "size" ) ) { 721 1.1 lukem arg += STRLENOF( "size" ); 722 1.1 lukem 723 1.1 lukem if ( arg[0] == '.' ) { 724 1.1 lukem arg++; 725 1.2 christos if ( STRSTART( arg, "soft=" ) ) { 726 1.1 lukem arg += STRLENOF( "soft=" ); 727 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 728 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 729 1.2 christos { 730 1.1 lukem limit->lms_s_soft = -1; 731 1.1 lukem 732 1.1 lukem } else { 733 1.1 lukem int soft; 734 1.1 lukem 735 1.1 lukem if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) { 736 1.1 lukem return( 1 ); 737 1.1 lukem } 738 1.1 lukem 739 1.1 lukem if ( soft == -1 ) { 740 1.1 lukem /* FIXME: use "unlimited" instead */ 741 1.1 lukem } 742 1.1 lukem 743 1.1 lukem limit->lms_s_soft = soft; 744 1.1 lukem } 745 1.1 lukem 746 1.2 christos } else if ( STRSTART( arg, "hard=" ) ) { 747 1.1 lukem arg += STRLENOF( "hard=" ); 748 1.1 lukem if ( strcasecmp( arg, "soft" ) == 0 ) { 749 1.1 lukem limit->lms_s_hard = 0; 750 1.1 lukem 751 1.2 christos } else if ( strcasecmp( arg, "unlimited" ) == 0 752 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 753 1.2 christos { 754 1.1 lukem limit->lms_s_hard = -1; 755 1.1 lukem 756 1.1 lukem } else { 757 1.1 lukem int hard; 758 1.1 lukem 759 1.1 lukem if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) { 760 1.1 lukem return( 1 ); 761 1.1 lukem } 762 1.1 lukem 763 1.1 lukem if ( hard == -1 ) { 764 1.1 lukem /* FIXME: use "unlimited" instead */ 765 1.1 lukem } 766 1.1 lukem 767 1.1 lukem if ( hard == 0 ) { 768 1.1 lukem /* FIXME: use "soft" instead */ 769 1.1 lukem } 770 1.1 lukem 771 1.1 lukem limit->lms_s_hard = hard; 772 1.1 lukem } 773 1.1 lukem 774 1.2 christos } else if ( STRSTART( arg, "unchecked=" ) ) { 775 1.1 lukem arg += STRLENOF( "unchecked=" ); 776 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 777 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 778 1.2 christos { 779 1.1 lukem limit->lms_s_unchecked = -1; 780 1.1 lukem 781 1.1 lukem } else if ( strcasecmp( arg, "disabled" ) == 0 ) { 782 1.1 lukem limit->lms_s_unchecked = 0; 783 1.1 lukem 784 1.1 lukem } else { 785 1.1 lukem int unchecked; 786 1.1 lukem 787 1.1 lukem if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) { 788 1.1 lukem return( 1 ); 789 1.1 lukem } 790 1.1 lukem 791 1.1 lukem if ( unchecked == -1 ) { 792 1.1 lukem /* FIXME: use "unlimited" instead */ 793 1.1 lukem } 794 1.1 lukem 795 1.1 lukem limit->lms_s_unchecked = unchecked; 796 1.1 lukem } 797 1.1 lukem 798 1.2 christos } else if ( STRSTART( arg, "pr=" ) ) { 799 1.1 lukem arg += STRLENOF( "pr=" ); 800 1.1 lukem if ( strcasecmp( arg, "noEstimate" ) == 0 ) { 801 1.1 lukem limit->lms_s_pr_hide = 1; 802 1.1 lukem 803 1.2 christos } else if ( strcasecmp( arg, "unlimited" ) == 0 804 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 805 1.2 christos { 806 1.1 lukem limit->lms_s_pr = -1; 807 1.1 lukem 808 1.1 lukem } else { 809 1.1 lukem int pr; 810 1.1 lukem 811 1.1 lukem if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) { 812 1.1 lukem return( 1 ); 813 1.1 lukem } 814 1.1 lukem 815 1.1 lukem if ( pr == -1 ) { 816 1.1 lukem /* FIXME: use "unlimited" instead */ 817 1.1 lukem } 818 1.1 lukem 819 1.1 lukem limit->lms_s_pr = pr; 820 1.1 lukem } 821 1.1 lukem 822 1.2 christos } else if ( STRSTART( arg, "prtotal=" ) ) { 823 1.1 lukem arg += STRLENOF( "prtotal=" ); 824 1.1 lukem 825 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 826 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 827 1.2 christos { 828 1.1 lukem limit->lms_s_pr_total = -1; 829 1.1 lukem 830 1.1 lukem } else if ( strcasecmp( arg, "disabled" ) == 0 ) { 831 1.1 lukem limit->lms_s_pr_total = -2; 832 1.1 lukem 833 1.1 lukem } else if ( strcasecmp( arg, "hard" ) == 0 ) { 834 1.1 lukem limit->lms_s_pr_total = 0; 835 1.1 lukem 836 1.1 lukem } else { 837 1.1 lukem int total; 838 1.1 lukem 839 1.1 lukem if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) { 840 1.1 lukem return( 1 ); 841 1.1 lukem } 842 1.1 lukem 843 1.1 lukem if ( total == -1 ) { 844 1.1 lukem /* FIXME: use "unlimited" instead */ 845 1.1 lukem } 846 1.1 lukem 847 1.1 lukem if ( total == 0 ) { 848 1.1 lukem /* FIXME: use "pr=disable" instead */ 849 1.1 lukem } 850 1.1 lukem 851 1.1 lukem limit->lms_s_pr_total = total; 852 1.1 lukem } 853 1.1 lukem 854 1.1 lukem } else { 855 1.1 lukem return( 1 ); 856 1.1 lukem } 857 1.1 lukem 858 1.1 lukem } else if ( arg[0] == '=' ) { 859 1.1 lukem arg++; 860 1.2 christos if ( strcasecmp( arg, "unlimited" ) == 0 861 1.2 christos || strcasecmp( arg, "none" ) == 0 ) 862 1.2 christos { 863 1.1 lukem limit->lms_s_soft = -1; 864 1.1 lukem 865 1.1 lukem } else { 866 1.1 lukem if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0 867 1.1 lukem || limit->lms_s_soft < -1 ) 868 1.1 lukem { 869 1.1 lukem return( 1 ); 870 1.1 lukem } 871 1.1 lukem } 872 1.1 lukem limit->lms_s_hard = 0; 873 1.1 lukem 874 1.1 lukem } else { 875 1.1 lukem return( 1 ); 876 1.1 lukem } 877 1.1 lukem } 878 1.1 lukem 879 1.1 lukem return 0; 880 1.1 lukem } 881 1.1 lukem 882 1.2 christos /* Helper macros for limits_unparse() and limits_unparse_one(): 883 1.2 christos * Write to ptr, but not past bufEnd. Move ptr past the new text. 884 1.2 christos * Return (success && enough room ? 0 : -1). 885 1.2 christos */ 886 1.2 christos #define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \ 887 1.2 christos (WHATSLEFT <= (bv).bv_len ? -1 : \ 888 1.2 christos ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0)) 889 1.2 christos #define ptr_APPEND_LIT(str) /* Append a string literal */ \ 890 1.2 christos (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \ 891 1.2 christos ((void) (ptr = lutil_strcopy( ptr, str )), 0)) 892 1.2 christos #define ptr_APPEND_FMT(args) /* Append formatted text */ \ 893 1.2 christos (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0)) 894 1.2 christos #define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg )) 895 1.2 christos #define WHATSLEFT ((ber_len_t) (bufEnd - ptr)) 896 1.1 lukem 897 1.1 lukem /* Caller must provide an adequately sized buffer in bv */ 898 1.1 lukem int 899 1.1 lukem limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen ) 900 1.1 lukem { 901 1.1 lukem struct berval btmp; 902 1.2 christos char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */ 903 1.2 christos ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */ 904 1.2 christos unsigned type, style; 905 1.2 christos int rc = 0; 906 1.1 lukem 907 1.1 lukem if ( !bv || !bv->bv_val ) return -1; 908 1.1 lukem 909 1.1 lukem ptr = bv->bv_val; 910 1.2 christos bufEnd = ptr + buflen; 911 1.2 christos type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK; 912 1.1 lukem 913 1.2 christos if ( type == SLAP_LIMITS_TYPE_GROUP ) { 914 1.2 christos rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"", 915 1.2 christos lim->lm_group_oc->soc_cname.bv_val, 916 1.2 christos lim->lm_group_ad->ad_cname.bv_val, 917 1.2 christos lim->lm_pat.bv_val )); 918 1.1 lukem } else { 919 1.2 christos style = lim->lm_flags & SLAP_LIMITS_MASK; 920 1.2 christos switch( style ) { 921 1.1 lukem case SLAP_LIMITS_ANONYMOUS: 922 1.1 lukem case SLAP_LIMITS_USERS: 923 1.1 lukem case SLAP_LIMITS_ANY: 924 1.2 christos rc = ptr_APPEND_BV( lmpats[style] ); 925 1.1 lukem break; 926 1.1 lukem case SLAP_LIMITS_UNDEFINED: 927 1.1 lukem case SLAP_LIMITS_EXACT: 928 1.1 lukem case SLAP_LIMITS_ONE: 929 1.1 lukem case SLAP_LIMITS_SUBTREE: 930 1.1 lukem case SLAP_LIMITS_CHILDREN: 931 1.1 lukem case SLAP_LIMITS_REGEX: 932 1.2 christos rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"", 933 1.2 christos type == SLAP_LIMITS_TYPE_SELF ? "" : "this.", 934 1.2 christos lmpats[style].bv_val, lim->lm_pat.bv_val )); 935 1.1 lukem break; 936 1.1 lukem } 937 1.1 lukem } 938 1.2 christos if ( rc == 0 ) { 939 1.2 christos bv->bv_len = ptr - bv->bv_val; 940 1.2 christos btmp.bv_val = ptr; 941 1.2 christos btmp.bv_len = 0; 942 1.2 christos rc = limits_unparse_one( &lim->lm_limits, 943 1.2 christos SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME, 944 1.2 christos &btmp, WHATSLEFT ); 945 1.2 christos if ( rc == 0 ) 946 1.2 christos bv->bv_len += btmp.bv_len; 947 1.1 lukem } 948 1.2 christos return rc; 949 1.1 lukem } 950 1.1 lukem 951 1.1 lukem /* Caller must provide an adequately sized buffer in bv */ 952 1.1 lukem int 953 1.2 christos limits_unparse_one( 954 1.2 christos struct slap_limits_set *lim, 955 1.2 christos int which, 956 1.2 christos struct berval *bv, 957 1.2 christos ber_len_t buflen ) 958 1.1 lukem { 959 1.2 christos char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */ 960 1.2 christos ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */ 961 1.1 lukem 962 1.1 lukem if ( !bv || !bv->bv_val ) return -1; 963 1.1 lukem 964 1.1 lukem ptr = bv->bv_val; 965 1.2 christos bufEnd = ptr + buflen; 966 1.1 lukem 967 1.1 lukem if ( which & SLAP_LIMIT_SIZE ) { 968 1.1 lukem if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) { 969 1.1 lukem 970 1.1 lukem /* If same as global limit, drop it */ 971 1.1 lukem if ( lim != &frontendDB->be_def_limit && 972 1.1 lukem lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft ) 973 1.1 lukem { 974 1.1 lukem goto s_hard; 975 1.1 lukem /* If there's also a hard limit, fully qualify this one */ 976 1.1 lukem } else if ( lim->lms_s_hard ) { 977 1.2 christos if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1; 978 1.1 lukem 979 1.1 lukem /* If doing both size & time, qualify this */ 980 1.1 lukem } else if ( which & SLAP_LIMIT_TIME ) { 981 1.2 christos if ( ptr_APPEND_LIT( " size=" ) ) return -1; 982 1.1 lukem } 983 1.1 lukem 984 1.2 christos if ( lim->lms_s_soft == -1 985 1.2 christos ? ptr_APPEND_LIT( "unlimited " ) 986 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) ) 987 1.2 christos return -1; 988 1.1 lukem } 989 1.1 lukem s_hard: 990 1.1 lukem if ( lim->lms_s_hard ) { 991 1.2 christos if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1; 992 1.2 christos if ( lim->lms_s_hard == -1 993 1.2 christos ? ptr_APPEND_LIT( "unlimited " ) 994 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) ) 995 1.2 christos return -1; 996 1.1 lukem } 997 1.1 lukem if ( lim->lms_s_unchecked != -1 ) { 998 1.2 christos if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1; 999 1.2 christos if ( lim->lms_s_unchecked == 0 1000 1.2 christos ? ptr_APPEND_LIT( "disabled " ) 1001 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) ) 1002 1.2 christos return -1; 1003 1.1 lukem } 1004 1.1 lukem if ( lim->lms_s_pr_hide ) { 1005 1.2 christos if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1; 1006 1.1 lukem } 1007 1.1 lukem if ( lim->lms_s_pr ) { 1008 1.2 christos if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1; 1009 1.2 christos if ( lim->lms_s_pr == -1 1010 1.2 christos ? ptr_APPEND_LIT( "unlimited " ) 1011 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) ) 1012 1.2 christos return -1; 1013 1.1 lukem } 1014 1.1 lukem if ( lim->lms_s_pr_total ) { 1015 1.2 christos if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1; 1016 1.2 christos if ( lim->lms_s_pr_total == -1 ? ptr_APPEND_LIT( "unlimited " ) 1017 1.2 christos : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " ) 1018 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) ) 1019 1.2 christos return -1; 1020 1.1 lukem } 1021 1.1 lukem } 1022 1.1 lukem 1023 1.1 lukem if ( which & SLAP_LIMIT_TIME ) { 1024 1.1 lukem if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) { 1025 1.1 lukem 1026 1.1 lukem /* If same as global limit, drop it */ 1027 1.1 lukem if ( lim != &frontendDB->be_def_limit && 1028 1.1 lukem lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft ) 1029 1.1 lukem { 1030 1.1 lukem goto t_hard; 1031 1.1 lukem 1032 1.1 lukem /* If there's also a hard limit, fully qualify this one */ 1033 1.1 lukem } else if ( lim->lms_t_hard ) { 1034 1.2 christos if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1; 1035 1.1 lukem 1036 1.1 lukem /* If doing both size & time, qualify this */ 1037 1.1 lukem } else if ( which & SLAP_LIMIT_SIZE ) { 1038 1.2 christos if ( ptr_APPEND_LIT( " time=" ) ) return -1; 1039 1.1 lukem } 1040 1.1 lukem 1041 1.2 christos if ( lim->lms_t_soft == -1 1042 1.2 christos ? ptr_APPEND_LIT( "unlimited " ) 1043 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) ) 1044 1.2 christos return -1; 1045 1.1 lukem } 1046 1.1 lukem t_hard: 1047 1.1 lukem if ( lim->lms_t_hard ) { 1048 1.2 christos if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1; 1049 1.2 christos if ( lim->lms_t_hard == -1 1050 1.2 christos ? ptr_APPEND_LIT( "unlimited " ) 1051 1.2 christos : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) ) 1052 1.2 christos return -1; 1053 1.1 lukem } 1054 1.1 lukem } 1055 1.1 lukem if ( ptr != bv->bv_val ) { 1056 1.1 lukem ptr--; 1057 1.1 lukem *ptr = '\0'; 1058 1.1 lukem bv->bv_len = ptr - bv->bv_val; 1059 1.1 lukem } 1060 1.1 lukem 1061 1.1 lukem return 0; 1062 1.1 lukem } 1063 1.1 lukem 1064 1.1 lukem int 1065 1.1 lukem limits_check( Operation *op, SlapReply *rs ) 1066 1.1 lukem { 1067 1.1 lukem assert( op != NULL ); 1068 1.1 lukem assert( rs != NULL ); 1069 1.1 lukem /* FIXME: should this be always true? */ 1070 1.1 lukem assert( op->o_tag == LDAP_REQ_SEARCH); 1071 1.1 lukem 1072 1.1 lukem /* protocol only allows 0..maxInt; 1073 1.1 lukem * 1074 1.1 lukem * internal searches: 1075 1.1 lukem * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits; 1076 1.1 lukem * - should use slimit = N and tlimit = SLAP_NO_LIMIT to 1077 1.1 lukem * indicate searches that should return exactly N matches, 1078 1.1 lukem * and handle errors thru a callback (see for instance 1079 1.1 lukem * slap_sasl_match() and slap_sasl2dn()) 1080 1.1 lukem */ 1081 1.1 lukem if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) { 1082 1.1 lukem return 0; 1083 1.1 lukem } 1084 1.1 lukem 1085 1.1 lukem /* allow root to set no limit */ 1086 1.1 lukem if ( be_isroot( op ) ) { 1087 1.1 lukem op->ors_limit = NULL; 1088 1.1 lukem 1089 1.1 lukem if ( op->ors_tlimit == 0 ) { 1090 1.1 lukem op->ors_tlimit = SLAP_NO_LIMIT; 1091 1.1 lukem } 1092 1.1 lukem 1093 1.1 lukem if ( op->ors_slimit == 0 ) { 1094 1.1 lukem op->ors_slimit = SLAP_NO_LIMIT; 1095 1.1 lukem } 1096 1.1 lukem 1097 1.1 lukem /* if paged results and slimit are requested */ 1098 1.1 lukem if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED && 1099 1.1 lukem op->ors_slimit != SLAP_NO_LIMIT ) { 1100 1.1 lukem PagedResultsState *ps = op->o_pagedresults_state; 1101 1.1 lukem int total = op->ors_slimit - ps->ps_count; 1102 1.1 lukem if ( total > 0 ) { 1103 1.1 lukem op->ors_slimit = total; 1104 1.1 lukem } else { 1105 1.1 lukem op->ors_slimit = 0; 1106 1.1 lukem } 1107 1.1 lukem } 1108 1.1 lukem 1109 1.1 lukem /* if not root, get appropriate limits */ 1110 1.1 lukem } else { 1111 1.2 christos ( void ) limits_get( op, &op->ors_limit ); 1112 1.1 lukem 1113 1.1 lukem assert( op->ors_limit != NULL ); 1114 1.1 lukem 1115 1.1 lukem /* if no limit is required, use soft limit */ 1116 1.1 lukem if ( op->ors_tlimit == 0 ) { 1117 1.1 lukem op->ors_tlimit = op->ors_limit->lms_t_soft; 1118 1.1 lukem 1119 1.1 lukem /* limit required: check if legal */ 1120 1.1 lukem } else { 1121 1.1 lukem if ( op->ors_limit->lms_t_hard == 0 ) { 1122 1.1 lukem if ( op->ors_limit->lms_t_soft > 0 1123 1.1 lukem && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) { 1124 1.1 lukem op->ors_tlimit = op->ors_limit->lms_t_soft; 1125 1.1 lukem } 1126 1.1 lukem 1127 1.1 lukem } else if ( op->ors_limit->lms_t_hard > 0 ) { 1128 1.1 lukem #ifdef ABOVE_HARD_LIMIT_IS_ERROR 1129 1.1 lukem if ( op->ors_tlimit == SLAP_MAX_LIMIT ) { 1130 1.1 lukem op->ors_tlimit = op->ors_limit->lms_t_hard; 1131 1.1 lukem 1132 1.1 lukem } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) { 1133 1.1 lukem /* error if exceeding hard limit */ 1134 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1135 1.1 lukem send_ldap_result( op, rs ); 1136 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1137 1.1 lukem return -1; 1138 1.1 lukem } 1139 1.1 lukem #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1140 1.1 lukem if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) { 1141 1.1 lukem op->ors_tlimit = op->ors_limit->lms_t_hard; 1142 1.1 lukem } 1143 1.1 lukem #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1144 1.1 lukem } 1145 1.1 lukem } 1146 1.1 lukem 1147 1.1 lukem /* else leave as is */ 1148 1.1 lukem 1149 1.1 lukem /* don't even get to backend if candidate check is disabled */ 1150 1.1 lukem if ( op->ors_limit->lms_s_unchecked == 0 ) { 1151 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1152 1.1 lukem send_ldap_result( op, rs ); 1153 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1154 1.1 lukem return -1; 1155 1.1 lukem } 1156 1.1 lukem 1157 1.1 lukem /* if paged results is requested */ 1158 1.1 lukem if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 1159 1.1 lukem int slimit = -2; 1160 1.1 lukem int pr_total; 1161 1.1 lukem PagedResultsState *ps = op->o_pagedresults_state; 1162 1.1 lukem 1163 1.1 lukem /* paged results is not allowed */ 1164 1.1 lukem if ( op->ors_limit->lms_s_pr_total == -2 ) { 1165 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1166 1.1 lukem rs->sr_text = "pagedResults control not allowed"; 1167 1.1 lukem send_ldap_result( op, rs ); 1168 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1169 1.1 lukem rs->sr_text = NULL; 1170 1.1 lukem return -1; 1171 1.1 lukem } 1172 1.1 lukem 1173 1.2 christos if ( op->ors_limit->lms_s_pr > 0 1174 1.2 christos && ps->ps_size > op->ors_limit->lms_s_pr ) 1175 1.2 christos { 1176 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1177 1.1 lukem rs->sr_text = "illegal pagedResults page size"; 1178 1.1 lukem send_ldap_result( op, rs ); 1179 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1180 1.1 lukem rs->sr_text = NULL; 1181 1.1 lukem return -1; 1182 1.1 lukem } 1183 1.1 lukem 1184 1.1 lukem if ( op->ors_limit->lms_s_pr_total == 0 ) { 1185 1.1 lukem if ( op->ors_limit->lms_s_hard == 0 ) { 1186 1.1 lukem pr_total = op->ors_limit->lms_s_soft; 1187 1.1 lukem } else { 1188 1.1 lukem pr_total = op->ors_limit->lms_s_hard; 1189 1.1 lukem } 1190 1.1 lukem } else { 1191 1.1 lukem pr_total = op->ors_limit->lms_s_pr_total; 1192 1.1 lukem } 1193 1.1 lukem 1194 1.1 lukem if ( pr_total == -1 ) { 1195 1.1 lukem if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) { 1196 1.1 lukem slimit = -1; 1197 1.1 lukem 1198 1.1 lukem } else { 1199 1.1 lukem slimit = op->ors_slimit - ps->ps_count; 1200 1.1 lukem } 1201 1.1 lukem 1202 1.1 lukem #ifdef ABOVE_HARD_LIMIT_IS_ERROR 1203 1.1 lukem } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT 1204 1.2 christos && ( op->ors_slimit == SLAP_NO_LIMIT 1205 1.2 christos || op->ors_slimit > pr_total ) ) 1206 1.1 lukem { 1207 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1208 1.1 lukem send_ldap_result( op, rs ); 1209 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1210 1.1 lukem return -1; 1211 1.1 lukem #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1212 1.1 lukem 1213 1.1 lukem } else { 1214 1.1 lukem /* if no limit is required, use soft limit */ 1215 1.1 lukem int total; 1216 1.1 lukem int slimit2; 1217 1.1 lukem 1218 1.2 christos /* first round of pagedResults: 1219 1.2 christos * set count to any appropriate limit */ 1220 1.1 lukem 1221 1.2 christos /* if the limit is set, check that it does 1222 1.2 christos * not violate any server-side limit */ 1223 1.1 lukem #ifdef ABOVE_HARD_LIMIT_IS_ERROR 1224 1.2 christos if ( op->ors_slimit == SLAP_MAX_LIMIT ) 1225 1.1 lukem #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1226 1.2 christos if ( op->ors_slimit == SLAP_MAX_LIMIT 1227 1.2 christos || op->ors_slimit > pr_total ) 1228 1.2 christos #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1229 1.2 christos { 1230 1.1 lukem slimit2 = op->ors_slimit = pr_total; 1231 1.1 lukem 1232 1.1 lukem } else if ( op->ors_slimit == 0 ) { 1233 1.1 lukem slimit2 = pr_total; 1234 1.1 lukem 1235 1.1 lukem } else { 1236 1.1 lukem slimit2 = op->ors_slimit; 1237 1.1 lukem } 1238 1.1 lukem 1239 1.1 lukem total = slimit2 - ps->ps_count; 1240 1.1 lukem 1241 1.1 lukem if ( total >= 0 ) { 1242 1.1 lukem if ( op->ors_limit->lms_s_pr > 0 ) { 1243 1.1 lukem /* use the smallest limit set by total/per page */ 1244 1.1 lukem if ( total < op->ors_limit->lms_s_pr ) { 1245 1.1 lukem slimit = total; 1246 1.1 lukem 1247 1.1 lukem } else { 1248 1.1 lukem /* use the perpage limit if any 1249 1.2 christos * NOTE: + 1 because given value must be legal */ 1250 1.1 lukem slimit = op->ors_limit->lms_s_pr + 1; 1251 1.1 lukem } 1252 1.1 lukem 1253 1.1 lukem } else { 1254 1.1 lukem /* use the total limit if any */ 1255 1.1 lukem slimit = total; 1256 1.1 lukem } 1257 1.1 lukem 1258 1.1 lukem } else if ( op->ors_limit->lms_s_pr > 0 ) { 1259 1.1 lukem /* use the perpage limit if any 1260 1.1 lukem * NOTE: + 1 because the given value must be legal */ 1261 1.1 lukem slimit = op->ors_limit->lms_s_pr + 1; 1262 1.1 lukem 1263 1.1 lukem } else { 1264 1.1 lukem /* use the standard hard/soft limit if any */ 1265 1.1 lukem slimit = op->ors_limit->lms_s_hard; 1266 1.1 lukem } 1267 1.1 lukem } 1268 1.1 lukem 1269 1.1 lukem /* if got any limit, use it */ 1270 1.1 lukem if ( slimit != -2 ) { 1271 1.1 lukem if ( op->ors_slimit == 0 ) { 1272 1.1 lukem op->ors_slimit = slimit; 1273 1.1 lukem 1274 1.1 lukem } else if ( slimit > 0 ) { 1275 1.1 lukem if ( op->ors_slimit - ps->ps_count > slimit ) { 1276 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1277 1.1 lukem send_ldap_result( op, rs ); 1278 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1279 1.1 lukem return -1; 1280 1.1 lukem } 1281 1.1 lukem op->ors_slimit = slimit; 1282 1.1 lukem 1283 1.1 lukem } else if ( slimit == 0 ) { 1284 1.1 lukem op->ors_slimit = 0; 1285 1.1 lukem } 1286 1.1 lukem 1287 1.1 lukem } else { 1288 1.1 lukem /* use the standard hard/soft limit if any */ 1289 1.1 lukem op->ors_slimit = pr_total; 1290 1.1 lukem } 1291 1.1 lukem 1292 1.1 lukem /* no limit requested: use soft, whatever it is */ 1293 1.1 lukem } else if ( op->ors_slimit == 0 ) { 1294 1.1 lukem op->ors_slimit = op->ors_limit->lms_s_soft; 1295 1.1 lukem 1296 1.1 lukem /* limit requested: check if legal */ 1297 1.1 lukem } else { 1298 1.1 lukem /* hard limit as soft (traditional behavior) */ 1299 1.1 lukem if ( op->ors_limit->lms_s_hard == 0 ) { 1300 1.1 lukem if ( op->ors_limit->lms_s_soft > 0 1301 1.1 lukem && op->ors_slimit > op->ors_limit->lms_s_soft ) { 1302 1.1 lukem op->ors_slimit = op->ors_limit->lms_s_soft; 1303 1.1 lukem } 1304 1.1 lukem 1305 1.1 lukem /* explicit hard limit: error if violated */ 1306 1.1 lukem } else if ( op->ors_limit->lms_s_hard > 0 ) { 1307 1.1 lukem #ifdef ABOVE_HARD_LIMIT_IS_ERROR 1308 1.1 lukem if ( op->ors_slimit == SLAP_MAX_LIMIT ) { 1309 1.1 lukem op->ors_slimit = op->ors_limit->lms_s_hard; 1310 1.1 lukem 1311 1.1 lukem } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) { 1312 1.1 lukem /* if limit exceeds hard, error */ 1313 1.1 lukem rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1314 1.1 lukem send_ldap_result( op, rs ); 1315 1.1 lukem rs->sr_err = LDAP_SUCCESS; 1316 1.1 lukem return -1; 1317 1.1 lukem } 1318 1.1 lukem #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1319 1.1 lukem if ( op->ors_slimit > op->ors_limit->lms_s_hard ) { 1320 1.1 lukem op->ors_slimit = op->ors_limit->lms_s_hard; 1321 1.1 lukem } 1322 1.1 lukem #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1323 1.1 lukem } 1324 1.1 lukem } 1325 1.1 lukem 1326 1.1 lukem /* else leave as is */ 1327 1.1 lukem } 1328 1.1 lukem 1329 1.1 lukem return 0; 1330 1.1 lukem } 1331 1.1 lukem 1332 1.1 lukem void 1333 1.2 christos limits_free_one( 1334 1.2 christos struct slap_limits *lm ) 1335 1.2 christos { 1336 1.2 christos if ( ( lm->lm_flags & SLAP_LIMITS_MASK ) == SLAP_LIMITS_REGEX ) 1337 1.2 christos regfree( &lm->lm_regex ); 1338 1.2 christos 1339 1.2 christos if ( !BER_BVISNULL( &lm->lm_pat ) ) 1340 1.2 christos ch_free( lm->lm_pat.bv_val ); 1341 1.2 christos 1342 1.2 christos ch_free( lm ); 1343 1.2 christos } 1344 1.2 christos 1345 1.2 christos void 1346 1.1 lukem limits_destroy( 1347 1.1 lukem struct slap_limits **lm ) 1348 1.1 lukem { 1349 1.1 lukem int i; 1350 1.1 lukem 1351 1.1 lukem if ( lm == NULL ) { 1352 1.1 lukem return; 1353 1.1 lukem } 1354 1.1 lukem 1355 1.1 lukem for ( i = 0; lm[ i ]; i++ ) { 1356 1.2 christos limits_free_one( lm[ i ] ); 1357 1.1 lukem } 1358 1.1 lukem 1359 1.1 lukem ch_free( lm ); 1360 1.1 lukem } 1361