1 /* $NetBSD: config.c,v 1.2 2020/08/11 13:15:40 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2020 The OpenLDAP Foundation. 7 * Portions Copyright 2001-2003 Pierangelo Masarati. 8 * Portions Copyright 1999-2003 Howard Chu. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 /* ACKNOWLEDGEMENTS: 20 * This work was initially developed by the Howard Chu for inclusion 21 * in OpenLDAP Software and subsequently enhanced by Pierangelo 22 * Masarati. 23 */ 24 25 #include <sys/cdefs.h> 26 __RCSID("$NetBSD: config.c,v 1.2 2020/08/11 13:15:40 christos Exp $"); 27 28 #include "portable.h" 29 30 #include <stdio.h> 31 #include <ctype.h> 32 33 #include <ac/string.h> 34 #include <ac/socket.h> 35 36 #include "slap.h" 37 #include "config.h" 38 #include "lutil.h" 39 #include "ldif.h" 40 #include "../back-ldap/back-ldap.h" 41 #include "back-meta.h" 42 43 #ifdef LDAP_DEVEL 44 #define SLAP_AUTH_DN 1 45 #endif 46 47 static ConfigDriver meta_back_cf_gen; 48 static ConfigLDAPadd meta_ldadd; 49 static ConfigCfAdd meta_cfadd; 50 51 static int ldap_back_map_config( 52 ConfigArgs *c, 53 struct ldapmap *oc_map, 54 struct ldapmap *at_map ); 55 56 /* Three sets of enums: 57 * 1) attrs that are only valid in the base config 58 * 2) attrs that are valid in base or target 59 * 3) attrs that are only valid in a target 60 */ 61 62 /* Base attrs */ 63 enum { 64 LDAP_BACK_CFG_CONN_TTL = 1, 65 LDAP_BACK_CFG_DNCACHE_TTL, 66 LDAP_BACK_CFG_IDLE_TIMEOUT, 67 LDAP_BACK_CFG_ONERR, 68 LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER, 69 LDAP_BACK_CFG_SINGLECONN, 70 LDAP_BACK_CFG_USETEMP, 71 LDAP_BACK_CFG_CONNPOOLMAX, 72 LDAP_BACK_CFG_LAST_BASE 73 }; 74 75 /* Base or target */ 76 enum { 77 LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE, 78 LDAP_BACK_CFG_CANCEL, 79 LDAP_BACK_CFG_CHASE, 80 LDAP_BACK_CFG_CLIENT_PR, 81 LDAP_BACK_CFG_DEFAULT_T, 82 LDAP_BACK_CFG_NETWORK_TIMEOUT, 83 LDAP_BACK_CFG_NOREFS, 84 LDAP_BACK_CFG_NOUNDEFFILTER, 85 LDAP_BACK_CFG_NRETRIES, 86 LDAP_BACK_CFG_QUARANTINE, 87 LDAP_BACK_CFG_REBIND, 88 LDAP_BACK_CFG_TIMEOUT, 89 LDAP_BACK_CFG_VERSION, 90 LDAP_BACK_CFG_ST_REQUEST, 91 LDAP_BACK_CFG_T_F, 92 LDAP_BACK_CFG_TLS, 93 LDAP_BACK_CFG_LAST_BOTH 94 }; 95 96 /* Target attrs */ 97 enum { 98 LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH, 99 LDAP_BACK_CFG_ACL_AUTHCDN, 100 LDAP_BACK_CFG_ACL_PASSWD, 101 LDAP_BACK_CFG_IDASSERT_AUTHZFROM, 102 LDAP_BACK_CFG_IDASSERT_BIND, 103 LDAP_BACK_CFG_REWRITE, 104 LDAP_BACK_CFG_SUFFIXM, 105 LDAP_BACK_CFG_MAP, 106 LDAP_BACK_CFG_SUBTREE_EX, 107 LDAP_BACK_CFG_SUBTREE_IN, 108 LDAP_BACK_CFG_PSEUDOROOTDN, 109 LDAP_BACK_CFG_PSEUDOROOTPW, 110 LDAP_BACK_CFG_KEEPALIVE, 111 LDAP_BACK_CFG_FILTER, 112 113 LDAP_BACK_CFG_LAST 114 }; 115 116 static ConfigTable metacfg[] = { 117 { "uri", "uri", 2, 0, 0, 118 ARG_MAGIC|LDAP_BACK_CFG_URI, 119 meta_back_cf_gen, "( OLcfgDbAt:0.14 " 120 "NAME 'olcDbURI' " 121 "DESC 'URI (list) for remote DSA' " 122 "SYNTAX OMsDirectoryString " 123 "SINGLE-VALUE )", 124 NULL, NULL }, 125 { "tls", "what", 2, 0, 0, 126 ARG_MAGIC|LDAP_BACK_CFG_TLS, 127 meta_back_cf_gen, "( OLcfgDbAt:3.1 " 128 "NAME 'olcDbStartTLS' " 129 "DESC 'StartTLS' " 130 "SYNTAX OMsDirectoryString " 131 "SINGLE-VALUE )", 132 NULL, NULL }, 133 { "acl-authcDN", "DN", 2, 2, 0, 134 ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN, 135 meta_back_cf_gen, "( OLcfgDbAt:3.2 " 136 "NAME 'olcDbACLAuthcDn' " 137 "DESC 'Remote ACL administrative identity' " 138 "OBSOLETE " 139 "SYNTAX OMsDN " 140 "SINGLE-VALUE )", 141 NULL, NULL }, 142 /* deprecated, will be removed; aliases "acl-authcDN" */ 143 { "binddn", "DN", 2, 2, 0, 144 ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN, 145 meta_back_cf_gen, NULL, NULL, NULL }, 146 { "acl-passwd", "cred", 2, 2, 0, 147 ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD, 148 meta_back_cf_gen, "( OLcfgDbAt:3.3 " 149 "NAME 'olcDbACLPasswd' " 150 "DESC 'Remote ACL administrative identity credentials' " 151 "OBSOLETE " 152 "SYNTAX OMsDirectoryString " 153 "SINGLE-VALUE )", 154 NULL, NULL }, 155 /* deprecated, will be removed; aliases "acl-passwd" */ 156 { "bindpw", "cred", 2, 2, 0, 157 ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD, 158 meta_back_cf_gen, NULL, NULL, NULL }, 159 { "idassert-bind", "args", 2, 0, 0, 160 ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND, 161 meta_back_cf_gen, "( OLcfgDbAt:3.7 " 162 "NAME 'olcDbIDAssertBind' " 163 "DESC 'Remote Identity Assertion administrative identity auth bind configuration' " 164 "SYNTAX OMsDirectoryString " 165 "SINGLE-VALUE )", 166 NULL, NULL }, 167 { "idassert-authzFrom", "authzRule", 2, 2, 0, 168 ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM, 169 meta_back_cf_gen, "( OLcfgDbAt:3.9 " 170 "NAME 'olcDbIDAssertAuthzFrom' " 171 "DESC 'Remote Identity Assertion authz rules' " 172 "EQUALITY caseIgnoreMatch " 173 "SYNTAX OMsDirectoryString " 174 "X-ORDERED 'VALUES' )", 175 NULL, NULL }, 176 { "rebind-as-user", "true|FALSE", 1, 2, 0, 177 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND, 178 meta_back_cf_gen, "( OLcfgDbAt:3.10 " 179 "NAME 'olcDbRebindAsUser' " 180 "DESC 'Rebind as user' " 181 "SYNTAX OMsBoolean " 182 "SINGLE-VALUE )", 183 NULL, NULL }, 184 { "chase-referrals", "true|FALSE", 2, 2, 0, 185 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE, 186 meta_back_cf_gen, "( OLcfgDbAt:3.11 " 187 "NAME 'olcDbChaseReferrals' " 188 "DESC 'Chase referrals' " 189 "SYNTAX OMsBoolean " 190 "SINGLE-VALUE )", 191 NULL, NULL }, 192 { "t-f-support", "true|FALSE|discover", 2, 2, 0, 193 ARG_MAGIC|LDAP_BACK_CFG_T_F, 194 meta_back_cf_gen, "( OLcfgDbAt:3.12 " 195 "NAME 'olcDbTFSupport' " 196 "DESC 'Absolute filters support' " 197 "SYNTAX OMsDirectoryString " 198 "SINGLE-VALUE )", 199 NULL, NULL }, 200 { "timeout", "timeout(list)", 2, 0, 0, 201 ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT, 202 meta_back_cf_gen, "( OLcfgDbAt:3.14 " 203 "NAME 'olcDbTimeout' " 204 "DESC 'Per-operation timeouts' " 205 "SYNTAX OMsDirectoryString " 206 "SINGLE-VALUE )", 207 NULL, NULL }, 208 { "idle-timeout", "timeout", 2, 2, 0, 209 ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT, 210 meta_back_cf_gen, "( OLcfgDbAt:3.15 " 211 "NAME 'olcDbIdleTimeout' " 212 "DESC 'connection idle timeout' " 213 "SYNTAX OMsDirectoryString " 214 "SINGLE-VALUE )", 215 NULL, NULL }, 216 { "conn-ttl", "ttl", 2, 2, 0, 217 ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL, 218 meta_back_cf_gen, "( OLcfgDbAt:3.16 " 219 "NAME 'olcDbConnTtl' " 220 "DESC 'connection ttl' " 221 "SYNTAX OMsDirectoryString " 222 "SINGLE-VALUE )", 223 NULL, NULL }, 224 { "network-timeout", "timeout", 2, 2, 0, 225 ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT, 226 meta_back_cf_gen, "( OLcfgDbAt:3.17 " 227 "NAME 'olcDbNetworkTimeout' " 228 "DESC 'connection network timeout' " 229 "SYNTAX OMsDirectoryString " 230 "SINGLE-VALUE )", 231 NULL, NULL }, 232 { "protocol-version", "version", 2, 2, 0, 233 ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION, 234 meta_back_cf_gen, "( OLcfgDbAt:3.18 " 235 "NAME 'olcDbProtocolVersion' " 236 "DESC 'protocol version' " 237 "SYNTAX OMsInteger " 238 "SINGLE-VALUE )", 239 NULL, NULL }, 240 { "single-conn", "true|FALSE", 2, 2, 0, 241 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN, 242 meta_back_cf_gen, "( OLcfgDbAt:3.19 " 243 "NAME 'olcDbSingleConn' " 244 "DESC 'cache a single connection per identity' " 245 "SYNTAX OMsBoolean " 246 "SINGLE-VALUE )", 247 NULL, NULL }, 248 { "cancel", "ABANDON|ignore|exop", 2, 2, 0, 249 ARG_MAGIC|LDAP_BACK_CFG_CANCEL, 250 meta_back_cf_gen, "( OLcfgDbAt:3.20 " 251 "NAME 'olcDbCancel' " 252 "DESC 'abandon/ignore/exop operations when appropriate' " 253 "SYNTAX OMsDirectoryString " 254 "SINGLE-VALUE )", 255 NULL, NULL }, 256 { "quarantine", "retrylist", 2, 2, 0, 257 ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE, 258 meta_back_cf_gen, "( OLcfgDbAt:3.21 " 259 "NAME 'olcDbQuarantine' " 260 "DESC 'Quarantine database if connection fails and retry according to rule' " 261 "SYNTAX OMsDirectoryString " 262 "SINGLE-VALUE )", 263 NULL, NULL }, 264 { "use-temporary-conn", "true|FALSE", 2, 2, 0, 265 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP, 266 meta_back_cf_gen, "( OLcfgDbAt:3.22 " 267 "NAME 'olcDbUseTemporaryConn' " 268 "DESC 'Use temporary connections if the cached one is busy' " 269 "SYNTAX OMsBoolean " 270 "SINGLE-VALUE )", 271 NULL, NULL }, 272 { "conn-pool-max", "<n>", 2, 2, 0, 273 ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX, 274 meta_back_cf_gen, "( OLcfgDbAt:3.23 " 275 "NAME 'olcDbConnectionPoolMax' " 276 "DESC 'Max size of privileged connections pool' " 277 "SYNTAX OMsInteger " 278 "SINGLE-VALUE )", 279 NULL, NULL }, 280 #ifdef SLAP_CONTROL_X_SESSION_TRACKING 281 { "session-tracking-request", "true|FALSE", 2, 2, 0, 282 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST, 283 meta_back_cf_gen, "( OLcfgDbAt:3.24 " 284 "NAME 'olcDbSessionTrackingRequest' " 285 "DESC 'Add session tracking control to proxied requests' " 286 "SYNTAX OMsBoolean " 287 "SINGLE-VALUE )", 288 NULL, NULL }, 289 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */ 290 { "norefs", "true|FALSE", 2, 2, 0, 291 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS, 292 meta_back_cf_gen, "( OLcfgDbAt:3.25 " 293 "NAME 'olcDbNoRefs' " 294 "DESC 'Do not return search reference responses' " 295 "SYNTAX OMsBoolean " 296 "SINGLE-VALUE )", 297 NULL, NULL }, 298 { "noundeffilter", "true|FALSE", 2, 2, 0, 299 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER, 300 meta_back_cf_gen, "( OLcfgDbAt:3.26 " 301 "NAME 'olcDbNoUndefFilter' " 302 "DESC 'Do not propagate undefined search filters' " 303 "SYNTAX OMsBoolean " 304 "SINGLE-VALUE )", 305 NULL, NULL }, 306 307 { "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ), 308 ARG_MAGIC|LDAP_BACK_CFG_REWRITE, 309 meta_back_cf_gen, "( OLcfgDbAt:3.101 " 310 "NAME 'olcDbRewrite' " 311 "DESC 'DN rewriting rules' " 312 "EQUALITY caseIgnoreMatch " 313 "SYNTAX OMsDirectoryString " 314 "X-ORDERED 'VALUES' )", 315 NULL, NULL }, 316 { "suffixmassage", "virtual> <real", 2, 3, 0, 317 ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM, 318 meta_back_cf_gen, NULL, NULL, NULL }, 319 320 { "map", "attribute|objectClass> [*|<local>] *|<remote", 3, 4, 0, 321 ARG_MAGIC|LDAP_BACK_CFG_MAP, 322 meta_back_cf_gen, "( OLcfgDbAt:3.102 " 323 "NAME 'olcDbMap' " 324 "DESC 'Map attribute and objectclass names' " 325 "EQUALITY caseIgnoreMatch " 326 "SYNTAX OMsDirectoryString " 327 "X-ORDERED 'VALUES' )", 328 NULL, NULL }, 329 330 { "subtree-exclude", "pattern", 2, 2, 0, 331 ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX, 332 meta_back_cf_gen, "( OLcfgDbAt:3.103 " 333 "NAME 'olcDbSubtreeExclude' " 334 "DESC 'DN of subtree to exclude from target' " 335 "EQUALITY caseIgnoreMatch " 336 "SYNTAX OMsDirectoryString )", 337 NULL, NULL }, 338 { "subtree-include", "pattern", 2, 2, 0, 339 ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN, 340 meta_back_cf_gen, "( OLcfgDbAt:3.104 " 341 "NAME 'olcDbSubtreeInclude' " 342 "DESC 'DN of subtree to include in target' " 343 "EQUALITY caseIgnoreMatch " 344 "SYNTAX OMsDirectoryString )", 345 NULL, NULL }, 346 { "default-target", "[none|<target ID>]", 1, 2, 0, 347 ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T, 348 meta_back_cf_gen, "( OLcfgDbAt:3.105 " 349 "NAME 'olcDbDefaultTarget' " 350 "DESC 'Specify the default target' " 351 "SYNTAX OMsDirectoryString " 352 "SINGLE-VALUE )", 353 NULL, NULL }, 354 { "dncache-ttl", "ttl", 2, 2, 0, 355 ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL, 356 meta_back_cf_gen, "( OLcfgDbAt:3.106 " 357 "NAME 'olcDbDnCacheTtl' " 358 "DESC 'dncache ttl' " 359 "SYNTAX OMsDirectoryString " 360 "SINGLE-VALUE )", 361 NULL, NULL }, 362 { "bind-timeout", "microseconds", 2, 2, 0, 363 ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT, 364 meta_back_cf_gen, "( OLcfgDbAt:3.107 " 365 "NAME 'olcDbBindTimeout' " 366 "DESC 'bind timeout' " 367 "SYNTAX OMsDirectoryString " 368 "SINGLE-VALUE )", 369 NULL, NULL }, 370 { "onerr", "CONTINUE|report|stop", 2, 2, 0, 371 ARG_MAGIC|LDAP_BACK_CFG_ONERR, 372 meta_back_cf_gen, "( OLcfgDbAt:3.108 " 373 "NAME 'olcDbOnErr' " 374 "DESC 'error handling' " 375 "SYNTAX OMsDirectoryString " 376 "SINGLE-VALUE )", 377 NULL, NULL }, 378 { "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0, 379 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER, 380 meta_back_cf_gen, "( OLcfgDbAt:3.109 " 381 "NAME 'olcDbPseudoRootBindDefer' " 382 "DESC 'error handling' " 383 "SYNTAX OMsBoolean " 384 "SINGLE-VALUE )", 385 NULL, NULL }, 386 { "root-bind-defer", "TRUE|false", 2, 2, 0, 387 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER, 388 meta_back_cf_gen, NULL, NULL, NULL }, 389 { "pseudorootdn", "dn", 2, 2, 0, 390 ARG_MAGIC|ARG_DN|LDAP_BACK_CFG_PSEUDOROOTDN, 391 meta_back_cf_gen, NULL, NULL, NULL }, 392 { "pseudorootpw", "password", 2, 2, 0, 393 ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTDN, 394 meta_back_cf_gen, NULL, NULL, NULL }, 395 { "nretries", "NEVER|forever|<number>", 2, 2, 0, 396 ARG_MAGIC|LDAP_BACK_CFG_NRETRIES, 397 meta_back_cf_gen, "( OLcfgDbAt:3.110 " 398 "NAME 'olcDbNretries' " 399 "DESC 'retry handling' " 400 "SYNTAX OMsDirectoryString " 401 "SINGLE-VALUE )", 402 NULL, NULL }, 403 { "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0, 404 ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR, 405 meta_back_cf_gen, "( OLcfgDbAt:3.111 " 406 "NAME 'olcDbClientPr' " 407 "DESC 'PagedResults handling' " 408 "SYNTAX OMsDirectoryString " 409 "SINGLE-VALUE )", 410 NULL, NULL }, 411 412 { "", "", 0, 0, 0, ARG_IGNORED, 413 NULL, "( OLcfgDbAt:3.100 NAME 'olcMetaSub' " 414 "DESC 'Placeholder to name a Target entry' " 415 "EQUALITY caseIgnoreMatch " 416 "SYNTAX OMsDirectoryString " 417 "SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL }, 418 419 { "keepalive", "keepalive", 2, 2, 0, 420 ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE, 421 meta_back_cf_gen, "( OLcfgDbAt:3.29 " 422 "NAME 'olcDbKeepalive' " 423 "DESC 'TCP keepalive' " 424 "SYNTAX OMsDirectoryString " 425 "SINGLE-VALUE )", 426 NULL, NULL }, 427 428 { "filter", "pattern", 2, 2, 0, 429 ARG_MAGIC|LDAP_BACK_CFG_FILTER, 430 meta_back_cf_gen, "( OLcfgDbAt:3.112 " 431 "NAME 'olcDbFilter' " 432 "DESC 'Filter regex pattern to include in target' " 433 "EQUALITY caseExactMatch " 434 "SYNTAX OMsDirectoryString )", 435 NULL, NULL }, 436 437 { NULL, NULL, 0, 0, 0, ARG_IGNORED, 438 NULL, NULL, NULL, NULL } 439 }; 440 441 #ifdef SLAP_CONTROL_X_SESSION_TRACKING 442 #define ST_ATTR "$ olcDbSessionTrackingRequest " 443 #else 444 #define ST_ATTR "" 445 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */ 446 447 #define COMMON_ATTRS \ 448 "$ olcDbBindTimeout " \ 449 "$ olcDbCancel " \ 450 "$ olcDbChaseReferrals " \ 451 "$ olcDbClientPr " \ 452 "$ olcDbDefaultTarget " \ 453 "$ olcDbNetworkTimeout " \ 454 "$ olcDbNoRefs " \ 455 "$ olcDbNoUndefFilter " \ 456 "$ olcDbNretries " \ 457 "$ olcDbProtocolVersion " \ 458 "$ olcDbQuarantine " \ 459 "$ olcDbRebindAsUser " \ 460 ST_ATTR \ 461 "$ olcDbStartTLS " \ 462 "$ olcDbTFSupport " 463 464 static ConfigOCs metaocs[] = { 465 { "( OLcfgDbOc:3.2 " 466 "NAME 'olcMetaConfig' " 467 "DESC 'Meta backend configuration' " 468 "SUP olcDatabaseConfig " 469 "MAY ( olcDbConnTtl " 470 "$ olcDbDnCacheTtl " 471 "$ olcDbIdleTimeout " 472 "$ olcDbOnErr " 473 "$ olcDbPseudoRootBindDefer " 474 "$ olcDbSingleConn " 475 "$ olcDbUseTemporaryConn " 476 "$ olcDbConnectionPoolMax " 477 478 /* defaults, may be overridden per-target */ 479 COMMON_ATTRS 480 ") )", 481 Cft_Database, metacfg, NULL, meta_cfadd }, 482 { "( OLcfgDbOc:3.3 " 483 "NAME 'olcMetaTargetConfig' " 484 "DESC 'Meta target configuration' " 485 "SUP olcConfig STRUCTURAL " 486 "MUST ( olcMetaSub $ olcDbURI ) " 487 "MAY ( olcDbACLAuthcDn " 488 "$ olcDbACLPasswd " 489 "$ olcDbIDAssertAuthzFrom " 490 "$ olcDbIDAssertBind " 491 "$ olcDbMap " 492 "$ olcDbRewrite " 493 "$ olcDbSubtreeExclude " 494 "$ olcDbSubtreeInclude " 495 "$ olcDbTimeout " 496 "$ olcDbKeepalive " 497 "$ olcDbFilter " 498 499 /* defaults may be inherited */ 500 COMMON_ATTRS 501 ") )", 502 Cft_Misc, metacfg, meta_ldadd }, 503 { NULL, 0, NULL } 504 }; 505 506 static int 507 meta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c ) 508 { 509 if ( p->ce_type != Cft_Database || !p->ce_be || 510 p->ce_be->be_cf_ocs != metaocs ) 511 return LDAP_CONSTRAINT_VIOLATION; 512 513 c->be = p->ce_be; 514 return LDAP_SUCCESS; 515 } 516 517 static int 518 meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c ) 519 { 520 metainfo_t *mi = ( metainfo_t * )c->be->be_private; 521 struct berval bv; 522 int i; 523 524 bv.bv_val = c->cr_msg; 525 for ( i=0; i<mi->mi_ntargets; i++ ) { 526 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), 527 "olcMetaSub=" SLAP_X_ORDERED_FMT "uri", i ); 528 c->ca_private = mi->mi_targets[i]; 529 c->valx = i; 530 config_build_entry( op, rs, p->e_private, c, 531 &bv, &metaocs[1], NULL ); 532 } 533 534 return LDAP_SUCCESS; 535 } 536 537 static int 538 meta_rwi_init( struct rewrite_info **rwm_rw ) 539 { 540 char *rargv[ 3 ]; 541 542 *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); 543 if ( *rwm_rw == NULL ) { 544 return -1; 545 } 546 /* 547 * the filter rewrite as a string must be disabled 548 * by default; it can be re-enabled by adding rules; 549 * this creates an empty rewriteContext 550 */ 551 rargv[ 0 ] = "rewriteContext"; 552 rargv[ 1 ] = "searchFilter"; 553 rargv[ 2 ] = NULL; 554 rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv ); 555 556 rargv[ 0 ] = "rewriteContext"; 557 rargv[ 1 ] = "default"; 558 rargv[ 2 ] = NULL; 559 rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv ); 560 561 return 0; 562 } 563 564 static int 565 meta_back_new_target( 566 metatarget_t **mtp ) 567 { 568 metatarget_t *mt; 569 570 *mtp = NULL; 571 572 mt = ch_calloc( sizeof( metatarget_t ), 1 ); 573 574 if ( meta_rwi_init( &mt->mt_rwmap.rwm_rw )) { 575 ch_free( mt ); 576 return -1; 577 } 578 579 ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex ); 580 581 mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY; 582 mt->mt_idassert_authmethod = LDAP_AUTH_NONE; 583 mt->mt_idassert_tls = SB_TLS_DEFAULT; 584 585 /* by default, use proxyAuthz control on each operation */ 586 mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE; 587 588 *mtp = mt; 589 590 return 0; 591 } 592 593 /* Validation for suffixmassage_config */ 594 static int 595 meta_suffixm_config( 596 ConfigArgs *c, 597 int argc, 598 char **argv, 599 metatarget_t *mt 600 ) 601 { 602 BackendDB *tmp_bd; 603 struct berval dn, nvnc, pvnc, nrnc, prnc; 604 int j, rc; 605 606 /* 607 * syntax: 608 * 609 * suffixmassage <suffix> <massaged suffix> 610 * 611 * the <suffix> field must be defined as a valid suffix 612 * (or suffixAlias?) for the current database; 613 * the <massaged suffix> shouldn't have already been 614 * defined as a valid suffix or suffixAlias for the 615 * current server 616 */ 617 618 ber_str2bv( argv[ 1 ], 0, 0, &dn ); 619 if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) { 620 snprintf( c->cr_msg, sizeof( c->cr_msg ), 621 "suffix \"%s\" is invalid", 622 argv[1] ); 623 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 624 return 1; 625 } 626 627 for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) { 628 if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) { 629 break; 630 } 631 } 632 633 if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) { 634 snprintf( c->cr_msg, sizeof( c->cr_msg ), 635 "suffix \"%s\" must be within the database naming context", 636 argv[1] ); 637 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 638 free( pvnc.bv_val ); 639 free( nvnc.bv_val ); 640 return 1; 641 } 642 643 ber_str2bv( argv[ 2 ], 0, 0, &dn ); 644 if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) { 645 snprintf( c->cr_msg, sizeof( c->cr_msg ), 646 "massaged suffix \"%s\" is invalid", 647 argv[2] ); 648 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 649 free( pvnc.bv_val ); 650 free( nvnc.bv_val ); 651 return 1; 652 } 653 654 tmp_bd = select_backend( &nrnc, 0 ); 655 if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) { 656 Debug( LDAP_DEBUG_ANY, 657 "%s: warning: <massaged suffix> \"%s\" resolves to this database, in " 658 "\"suffixMassage <suffix> <massaged suffix>\"\n", 659 c->log, prnc.bv_val, 0 ); 660 } 661 662 /* 663 * The suffix massaging is emulated by means of the 664 * rewrite capabilities 665 */ 666 rc = suffix_massage_config( mt->mt_rwmap.rwm_rw, 667 &pvnc, &nvnc, &prnc, &nrnc ); 668 669 free( pvnc.bv_val ); 670 free( nvnc.bv_val ); 671 free( prnc.bv_val ); 672 free( nrnc.bv_val ); 673 674 return rc; 675 } 676 677 static int 678 slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out ) 679 { 680 int i; 681 BerVarray bva = NULL; 682 char ibuf[32], *ptr; 683 struct berval idx; 684 685 assert( in != NULL ); 686 687 for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) 688 /* count'em */ ; 689 690 if ( i == 0 ) { 691 return 1; 692 } 693 694 idx.bv_val = ibuf; 695 696 bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) ); 697 BER_BVZERO( &bva[ 0 ] ); 698 699 for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) { 700 idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i ); 701 if ( idx.bv_len >= sizeof( ibuf ) ) { 702 ber_bvarray_free( bva ); 703 return 1; 704 } 705 706 bva[i].bv_len = idx.bv_len + in[i].bv_len; 707 bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 ); 708 ptr = lutil_strcopy( bva[i].bv_val, ibuf ); 709 ptr = lutil_strcopy( ptr, in[i].bv_val ); 710 *ptr = '\0'; 711 BER_BVZERO( &bva[ i + 1 ] ); 712 } 713 714 *out = bva; 715 return 0; 716 } 717 718 int 719 meta_subtree_free( metasubtree_t *ms ) 720 { 721 switch ( ms->ms_type ) { 722 case META_ST_SUBTREE: 723 case META_ST_SUBORDINATE: 724 ber_memfree( ms->ms_dn.bv_val ); 725 break; 726 727 case META_ST_REGEX: 728 regfree( &ms->ms_regex ); 729 ber_memfree( ms->ms_regex_pattern.bv_val ); 730 break; 731 732 default: 733 return -1; 734 } 735 736 ch_free( ms ); 737 return 0; 738 } 739 740 int 741 meta_subtree_destroy( metasubtree_t *ms ) 742 { 743 if ( ms->ms_next ) { 744 meta_subtree_destroy( ms->ms_next ); 745 } 746 747 return meta_subtree_free( ms ); 748 } 749 750 static void 751 meta_filter_free( metafilter_t *mf ) 752 { 753 regfree( &mf->mf_regex ); 754 ber_memfree( mf->mf_regex_pattern.bv_val ); 755 ch_free( mf ); 756 } 757 758 void 759 meta_filter_destroy( metafilter_t *mf ) 760 { 761 if ( mf->mf_next ) 762 meta_filter_destroy( mf->mf_next ); 763 meta_filter_free( mf ); 764 } 765 766 static struct berval st_styles[] = { 767 BER_BVC("subtree"), 768 BER_BVC("children"), 769 BER_BVC("regex") 770 }; 771 772 static int 773 meta_subtree_unparse( 774 ConfigArgs *c, 775 metatarget_t *mt ) 776 { 777 metasubtree_t *ms; 778 struct berval bv, *style; 779 780 if ( !mt->mt_subtree ) 781 return 1; 782 783 /* can only be one of exclude or include */ 784 if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) 785 return 1; 786 787 bv.bv_val = c->cr_msg; 788 for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) { 789 if (ms->ms_type == META_ST_SUBTREE) 790 style = &st_styles[0]; 791 else if ( ms->ms_type == META_ST_SUBORDINATE ) 792 style = &st_styles[1]; 793 else if ( ms->ms_type == META_ST_REGEX ) 794 style = &st_styles[2]; 795 else { 796 assert(0); 797 continue; 798 } 799 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), 800 "dn.%s:%s", style->bv_val, ms->ms_dn.bv_val ); 801 value_add_one( &c->rvalue_vals, &bv ); 802 } 803 return 0; 804 } 805 806 static int 807 meta_subtree_config( 808 metatarget_t *mt, 809 ConfigArgs *c ) 810 { 811 meta_st_t type = META_ST_SUBTREE; 812 char *pattern; 813 struct berval ndn = BER_BVNULL; 814 metasubtree_t *ms = NULL; 815 816 if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) { 817 if ( mt->mt_subtree && !mt->mt_subtree_exclude ) { 818 snprintf( c->cr_msg, sizeof(c->cr_msg), 819 "\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" ); 820 return 1; 821 } 822 823 mt->mt_subtree_exclude = 1; 824 825 } else { 826 if ( mt->mt_subtree && mt->mt_subtree_exclude ) { 827 snprintf( c->cr_msg, sizeof(c->cr_msg), 828 "\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" ); 829 return 1; 830 } 831 } 832 833 pattern = c->argv[1]; 834 if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) { 835 char *style; 836 837 pattern = &pattern[STRLENOF( "dn")]; 838 839 if ( pattern[0] == '.' ) { 840 style = &pattern[1]; 841 842 if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) { 843 type = META_ST_SUBTREE; 844 pattern = &style[STRLENOF( "subtree" )]; 845 846 } else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) { 847 type = META_ST_SUBORDINATE; 848 pattern = &style[STRLENOF( "children" )]; 849 850 } else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) { 851 type = META_ST_SUBTREE; 852 pattern = &style[STRLENOF( "sub" )]; 853 854 } else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) { 855 type = META_ST_REGEX; 856 pattern = &style[STRLENOF( "regex" )]; 857 858 } else { 859 snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" ); 860 return 1; 861 } 862 } 863 864 if ( pattern[0] != ':' ) { 865 snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" ); 866 return 1; 867 } 868 pattern++; 869 } 870 871 switch ( type ) { 872 case META_ST_SUBTREE: 873 case META_ST_SUBORDINATE: { 874 struct berval dn; 875 876 ber_str2bv( pattern, 0, 0, &dn ); 877 if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) 878 != LDAP_SUCCESS ) 879 { 880 snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern ); 881 return 1; 882 } 883 884 if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) { 885 snprintf( c->cr_msg, sizeof(c->cr_msg), 886 "DN=\"%s\" is not a subtree of target \"%s\"", 887 pattern, mt->mt_nsuffix.bv_val ); 888 ber_memfree( ndn.bv_val ); 889 return( 1 ); 890 } 891 } break; 892 893 default: 894 /* silence warnings */ 895 break; 896 } 897 898 ms = ch_calloc( sizeof( metasubtree_t ), 1 ); 899 ms->ms_type = type; 900 901 switch ( ms->ms_type ) { 902 case META_ST_SUBTREE: 903 case META_ST_SUBORDINATE: 904 ms->ms_dn = ndn; 905 break; 906 907 case META_ST_REGEX: { 908 int rc; 909 910 rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE ); 911 if ( rc != 0 ) { 912 char regerr[ SLAP_TEXT_BUFLEN ]; 913 914 regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) ); 915 916 snprintf( c->cr_msg, sizeof( c->cr_msg ), 917 "regular expression \"%s\" bad because of %s", 918 pattern, regerr ); 919 ch_free( ms ); 920 return 1; 921 } 922 ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern ); 923 } break; 924 } 925 926 if ( mt->mt_subtree == NULL ) { 927 mt->mt_subtree = ms; 928 929 } else { 930 metasubtree_t **msp; 931 932 for ( msp = &mt->mt_subtree; *msp; ) { 933 switch ( ms->ms_type ) { 934 case META_ST_SUBTREE: 935 switch ( (*msp)->ms_type ) { 936 case META_ST_SUBTREE: 937 if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) { 938 metasubtree_t *tmp = *msp; 939 Debug( LDAP_DEBUG_CONFIG, 940 "%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n", 941 c->log, pattern, (*msp)->ms_dn.bv_val ); 942 *msp = (*msp)->ms_next; 943 tmp->ms_next = NULL; 944 meta_subtree_destroy( tmp ); 945 continue; 946 947 } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) { 948 Debug( LDAP_DEBUG_CONFIG, 949 "%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n", 950 c->log, (*msp)->ms_dn.bv_val, pattern ); 951 meta_subtree_destroy( ms ); 952 ms = NULL; 953 return( 0 ); 954 } 955 break; 956 957 case META_ST_SUBORDINATE: 958 if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) { 959 metasubtree_t *tmp = *msp; 960 Debug( LDAP_DEBUG_CONFIG, 961 "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n", 962 c->log, pattern, (*msp)->ms_dn.bv_val ); 963 *msp = (*msp)->ms_next; 964 tmp->ms_next = NULL; 965 meta_subtree_destroy( tmp ); 966 continue; 967 968 } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) { 969 Debug( LDAP_DEBUG_CONFIG, 970 "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n", 971 c->log, (*msp)->ms_dn.bv_val, pattern ); 972 meta_subtree_destroy( ms ); 973 ms = NULL; 974 return( 0 ); 975 } 976 break; 977 978 case META_ST_REGEX: 979 if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) { 980 Debug( LDAP_DEBUG_CONFIG, 981 "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n", 982 c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val ); 983 } 984 break; 985 } 986 break; 987 988 case META_ST_SUBORDINATE: 989 switch ( (*msp)->ms_type ) { 990 case META_ST_SUBTREE: 991 if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) { 992 metasubtree_t *tmp = *msp; 993 Debug( LDAP_DEBUG_CONFIG, 994 "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n", 995 c->log, pattern, (*msp)->ms_dn.bv_val ); 996 *msp = (*msp)->ms_next; 997 tmp->ms_next = NULL; 998 meta_subtree_destroy( tmp ); 999 continue; 1000 1001 } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) { 1002 Debug( LDAP_DEBUG_CONFIG, 1003 "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n", 1004 c->log, (*msp)->ms_dn.bv_val, pattern ); 1005 meta_subtree_destroy( ms ); 1006 ms = NULL; 1007 return( 0 ); 1008 } 1009 break; 1010 1011 case META_ST_SUBORDINATE: 1012 if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) { 1013 metasubtree_t *tmp = *msp; 1014 Debug( LDAP_DEBUG_CONFIG, 1015 "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n", 1016 c->log, pattern, (*msp)->ms_dn.bv_val ); 1017 *msp = (*msp)->ms_next; 1018 tmp->ms_next = NULL; 1019 meta_subtree_destroy( tmp ); 1020 continue; 1021 1022 } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) { 1023 Debug( LDAP_DEBUG_CONFIG, 1024 "%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n", 1025 c->log, (*msp)->ms_dn.bv_val, pattern ); 1026 meta_subtree_destroy( ms ); 1027 ms = NULL; 1028 return( 0 ); 1029 } 1030 break; 1031 1032 case META_ST_REGEX: 1033 if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) { 1034 Debug( LDAP_DEBUG_CONFIG, 1035 "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n", 1036 c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val ); 1037 } 1038 break; 1039 } 1040 break; 1041 1042 case META_ST_REGEX: 1043 switch ( (*msp)->ms_type ) { 1044 case META_ST_SUBTREE: 1045 case META_ST_SUBORDINATE: 1046 if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) { 1047 Debug( LDAP_DEBUG_CONFIG, 1048 "%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n", 1049 c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val ); 1050 } 1051 break; 1052 1053 case META_ST_REGEX: 1054 /* no check possible */ 1055 break; 1056 } 1057 break; 1058 } 1059 1060 msp = &(*msp)->ms_next; 1061 } 1062 1063 *msp = ms; 1064 } 1065 1066 return 0; 1067 } 1068 1069 static slap_verbmasks idassert_mode[] = { 1070 { BER_BVC("self"), LDAP_BACK_IDASSERT_SELF }, 1071 { BER_BVC("anonymous"), LDAP_BACK_IDASSERT_ANONYMOUS }, 1072 { BER_BVC("none"), LDAP_BACK_IDASSERT_NOASSERT }, 1073 { BER_BVC("legacy"), LDAP_BACK_IDASSERT_LEGACY }, 1074 { BER_BVNULL, 0 } 1075 }; 1076 1077 static slap_verbmasks tls_mode[] = { 1078 { BER_BVC( "propagate" ), LDAP_BACK_F_TLS_PROPAGATE_MASK }, 1079 { BER_BVC( "try-propagate" ), LDAP_BACK_F_PROPAGATE_TLS }, 1080 { BER_BVC( "start" ), LDAP_BACK_F_TLS_USE_MASK }, 1081 { BER_BVC( "try-start" ), LDAP_BACK_F_USE_TLS }, 1082 { BER_BVC( "ldaps" ), LDAP_BACK_F_TLS_LDAPS }, 1083 { BER_BVC( "none" ), LDAP_BACK_F_NONE }, 1084 { BER_BVNULL, 0 } 1085 }; 1086 1087 static slap_verbmasks t_f_mode[] = { 1088 { BER_BVC( "yes" ), LDAP_BACK_F_T_F }, 1089 { BER_BVC( "discover" ), LDAP_BACK_F_T_F_DISCOVER }, 1090 { BER_BVC( "no" ), LDAP_BACK_F_NONE }, 1091 { BER_BVNULL, 0 } 1092 }; 1093 1094 static slap_verbmasks cancel_mode[] = { 1095 { BER_BVC( "ignore" ), LDAP_BACK_F_CANCEL_IGNORE }, 1096 { BER_BVC( "exop" ), LDAP_BACK_F_CANCEL_EXOP }, 1097 { BER_BVC( "exop-discover" ), LDAP_BACK_F_CANCEL_EXOP_DISCOVER }, 1098 { BER_BVC( "abandon" ), LDAP_BACK_F_CANCEL_ABANDON }, 1099 { BER_BVNULL, 0 } 1100 }; 1101 1102 static slap_verbmasks onerr_mode[] = { 1103 { BER_BVC( "stop" ), META_BACK_F_ONERR_STOP }, 1104 { BER_BVC( "report" ), META_BACK_F_ONERR_REPORT }, 1105 { BER_BVC( "continue" ), LDAP_BACK_F_NONE }, 1106 { BER_BVNULL, 0 } 1107 }; 1108 1109 /* see enum in slap.h */ 1110 static slap_cf_aux_table timeout_table[] = { 1111 { BER_BVC("bind="), SLAP_OP_BIND * sizeof( time_t ), 'u', 0, NULL }, 1112 /* unbind makes no sense */ 1113 { BER_BVC("add="), SLAP_OP_ADD * sizeof( time_t ), 'u', 0, NULL }, 1114 { BER_BVC("delete="), SLAP_OP_DELETE * sizeof( time_t ), 'u', 0, NULL }, 1115 { BER_BVC("modrdn="), SLAP_OP_MODRDN * sizeof( time_t ), 'u', 0, NULL }, 1116 { BER_BVC("modify="), SLAP_OP_MODIFY * sizeof( time_t ), 'u', 0, NULL }, 1117 { BER_BVC("compare="), SLAP_OP_COMPARE * sizeof( time_t ), 'u', 0, NULL }, 1118 { BER_BVC("search="), SLAP_OP_SEARCH * sizeof( time_t ), 'u', 0, NULL }, 1119 /* abandon makes little sense */ 1120 #if 0 /* not implemented yet */ 1121 { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ), 'u', 0, NULL }, 1122 #endif 1123 { BER_BVNULL, 0, 0, 0, NULL } 1124 }; 1125 1126 static int 1127 meta_cf_cleanup( ConfigArgs *c ) 1128 { 1129 metainfo_t *mi = ( metainfo_t * )c->be->be_private; 1130 metatarget_t *mt = c->ca_private; 1131 1132 return meta_target_finish( mi, mt, c->log, c->cr_msg, sizeof( c->cr_msg )); 1133 } 1134 1135 static int 1136 meta_back_cf_gen( ConfigArgs *c ) 1137 { 1138 metainfo_t *mi = ( metainfo_t * )c->be->be_private; 1139 metatarget_t *mt; 1140 metacommon_t *mc; 1141 1142 int i, rc = 0; 1143 1144 assert( mi != NULL ); 1145 1146 if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) { 1147 if ( !mi ) 1148 return 1; 1149 1150 if ( c->table == Cft_Database ) { 1151 mt = NULL; 1152 mc = &mi->mi_mc; 1153 } else { 1154 mt = c->ca_private; 1155 mc = &mt->mt_mc; 1156 } 1157 } 1158 1159 if ( c->op == SLAP_CONFIG_EMIT ) { 1160 struct berval bv = BER_BVNULL; 1161 1162 switch( c->type ) { 1163 /* Base attrs */ 1164 case LDAP_BACK_CFG_CONN_TTL: 1165 if ( mi->mi_conn_ttl == 0 ) { 1166 return 1; 1167 } else { 1168 char buf[ SLAP_TEXT_BUFLEN ]; 1169 1170 lutil_unparse_time( buf, sizeof( buf ), mi->mi_conn_ttl ); 1171 ber_str2bv( buf, 0, 0, &bv ); 1172 value_add_one( &c->rvalue_vals, &bv ); 1173 } 1174 break; 1175 1176 case LDAP_BACK_CFG_DNCACHE_TTL: 1177 if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) { 1178 return 1; 1179 } else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) { 1180 BER_BVSTR( &bv, "forever" ); 1181 } else { 1182 char buf[ SLAP_TEXT_BUFLEN ]; 1183 1184 lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl ); 1185 ber_str2bv( buf, 0, 0, &bv ); 1186 } 1187 value_add_one( &c->rvalue_vals, &bv ); 1188 break; 1189 1190 case LDAP_BACK_CFG_IDLE_TIMEOUT: 1191 if ( mi->mi_idle_timeout == 0 ) { 1192 return 1; 1193 } else { 1194 char buf[ SLAP_TEXT_BUFLEN ]; 1195 1196 lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout ); 1197 ber_str2bv( buf, 0, 0, &bv ); 1198 value_add_one( &c->rvalue_vals, &bv ); 1199 } 1200 break; 1201 1202 case LDAP_BACK_CFG_ONERR: 1203 enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv ); 1204 if ( BER_BVISNULL( &bv )) { 1205 rc = 1; 1206 } else { 1207 value_add_one( &c->rvalue_vals, &bv ); 1208 } 1209 break; 1210 1211 case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER: 1212 c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi ); 1213 break; 1214 1215 case LDAP_BACK_CFG_SINGLECONN: 1216 c->value_int = LDAP_BACK_SINGLECONN( mi ); 1217 break; 1218 1219 case LDAP_BACK_CFG_USETEMP: 1220 c->value_int = LDAP_BACK_USE_TEMPORARIES( mi ); 1221 break; 1222 1223 case LDAP_BACK_CFG_CONNPOOLMAX: 1224 c->value_int = mi->mi_conn_priv_max; 1225 break; 1226 1227 /* common attrs */ 1228 case LDAP_BACK_CFG_BIND_TIMEOUT: 1229 if ( mc->mc_bind_timeout.tv_sec == 0 && 1230 mc->mc_bind_timeout.tv_usec == 0 ) { 1231 return 1; 1232 } else { 1233 c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL + 1234 mc->mc_bind_timeout.tv_usec; 1235 } 1236 break; 1237 1238 case LDAP_BACK_CFG_CANCEL: { 1239 slap_mask_t mask = LDAP_BACK_F_CANCEL_MASK2; 1240 1241 if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) { 1242 mask &= ~LDAP_BACK_F_CANCEL_EXOP; 1243 } 1244 enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv ); 1245 if ( BER_BVISNULL( &bv ) ) { 1246 /* there's something wrong... */ 1247 assert( 0 ); 1248 rc = 1; 1249 1250 } else { 1251 value_add_one( &c->rvalue_vals, &bv ); 1252 } 1253 } break; 1254 1255 case LDAP_BACK_CFG_CHASE: 1256 c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc); 1257 break; 1258 1259 #ifdef SLAPD_META_CLIENT_PR 1260 case LDAP_BACK_CFG_CLIENT_PR: 1261 if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) { 1262 return 1; 1263 } else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) { 1264 BER_BVSTR( &bv, "accept-unsolicited" ); 1265 } else { 1266 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps ); 1267 bv.bv_val = c->cr_msg; 1268 } 1269 value_add_one( &c->rvalue_vals, &bv ); 1270 break; 1271 #endif /* SLAPD_META_CLIENT_PR */ 1272 1273 case LDAP_BACK_CFG_DEFAULT_T: 1274 if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE ) 1275 return 1; 1276 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget ); 1277 bv.bv_val = c->cr_msg; 1278 value_add_one( &c->rvalue_vals, &bv ); 1279 break; 1280 1281 case LDAP_BACK_CFG_NETWORK_TIMEOUT: 1282 if ( mc->mc_network_timeout == 0 ) { 1283 return 1; 1284 } 1285 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld", 1286 mc->mc_network_timeout ); 1287 bv.bv_val = c->cr_msg; 1288 value_add_one( &c->rvalue_vals, &bv ); 1289 break; 1290 1291 case LDAP_BACK_CFG_NOREFS: 1292 c->value_int = META_BACK_CMN_NOREFS(mc); 1293 break; 1294 1295 case LDAP_BACK_CFG_NOUNDEFFILTER: 1296 c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc); 1297 break; 1298 1299 case LDAP_BACK_CFG_NRETRIES: 1300 if ( mc->mc_nretries == META_RETRY_FOREVER ) { 1301 BER_BVSTR( &bv, "forever" ); 1302 } else if ( mc->mc_nretries == META_RETRY_NEVER ) { 1303 BER_BVSTR( &bv, "never" ); 1304 } else { 1305 bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", 1306 mc->mc_nretries ); 1307 bv.bv_val = c->cr_msg; 1308 } 1309 value_add_one( &c->rvalue_vals, &bv ); 1310 break; 1311 1312 case LDAP_BACK_CFG_QUARANTINE: 1313 if ( !META_BACK_CMN_QUARANTINE( mc )) { 1314 rc = 1; 1315 break; 1316 } 1317 rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv ); 1318 if ( rc == 0 ) { 1319 ber_bvarray_add( &c->rvalue_vals, &bv ); 1320 } 1321 break; 1322 1323 case LDAP_BACK_CFG_REBIND: 1324 c->value_int = META_BACK_CMN_SAVECRED(mc); 1325 break; 1326 1327 case LDAP_BACK_CFG_TIMEOUT: 1328 for ( i = 0; i < SLAP_OP_LAST; i++ ) { 1329 if ( mc->mc_timeout[ i ] != 0 ) { 1330 break; 1331 } 1332 } 1333 1334 if ( i == SLAP_OP_LAST ) { 1335 return 1; 1336 } 1337 1338 BER_BVZERO( &bv ); 1339 slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table ); 1340 1341 if ( BER_BVISNULL( &bv ) ) { 1342 return 1; 1343 } 1344 1345 for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ ) 1346 /* count spaces */ ; 1347 1348 if ( i ) { 1349 bv.bv_len -= i; 1350 AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ], 1351 bv.bv_len + 1 ); 1352 } 1353 1354 ber_bvarray_add( &c->rvalue_vals, &bv ); 1355 break; 1356 1357 case LDAP_BACK_CFG_VERSION: 1358 if ( mc->mc_version == 0 ) 1359 return 1; 1360 c->value_int = mc->mc_version; 1361 break; 1362 1363 #ifdef SLAP_CONTROL_X_SESSION_TRACKING 1364 case LDAP_BACK_CFG_ST_REQUEST: 1365 c->value_int = META_BACK_CMN_ST_REQUEST( mc ); 1366 break; 1367 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */ 1368 1369 case LDAP_BACK_CFG_T_F: 1370 enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv ); 1371 if ( BER_BVISNULL( &bv ) ) { 1372 /* there's something wrong... */ 1373 assert( 0 ); 1374 rc = 1; 1375 1376 } else { 1377 value_add_one( &c->rvalue_vals, &bv ); 1378 } 1379 break; 1380 1381 case LDAP_BACK_CFG_TLS: { 1382 struct berval bc = BER_BVNULL, bv2; 1383 1384 if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) { 1385 rc = 1; 1386 break; 1387 } 1388 enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv ); 1389 assert( !BER_BVISNULL( &bv ) ); 1390 1391 if ( mt ) { 1392 bindconf_tls_unparse( &mt->mt_tls, &bc ); 1393 } 1394 1395 if ( !BER_BVISEMPTY( &bc )) { 1396 bv2.bv_len = bv.bv_len + bc.bv_len + 1; 1397 bv2.bv_val = ch_malloc( bv2.bv_len + 1 ); 1398 strcpy( bv2.bv_val, bv.bv_val ); 1399 bv2.bv_val[bv.bv_len] = ' '; 1400 strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val ); 1401 ber_memfree( bc.bv_val ); 1402 ber_bvarray_add( &c->rvalue_vals, &bv2 ); 1403 } else { 1404 value_add_one( &c->rvalue_vals, &bv ); 1405 } 1406 } break; 1407 1408 /* target attrs */ 1409 case LDAP_BACK_CFG_URI: { 1410 char *p2, *p1 = strchr( mt->mt_uri, ' ' ); 1411 bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len; 1412 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 1413 p2 = bv.bv_val; 1414 *p2++ = '"'; 1415 if ( p1 ) { 1416 p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri ); 1417 } else { 1418 p2 = lutil_strcopy( p2, mt->mt_uri ); 1419 } 1420 *p2++ = '/'; 1421 p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val ); 1422 *p2++ = '"'; 1423 if ( p1 ) { 1424 strcpy( p2, p1 ); 1425 } 1426 ber_bvarray_add( &c->rvalue_vals, &bv ); 1427 } break; 1428 1429 case LDAP_BACK_CFG_ACL_AUTHCDN: 1430 case LDAP_BACK_CFG_ACL_PASSWD: 1431 /* FIXME no point here, there is no code implementing 1432 * their features. Was this supposed to implement 1433 * acl-bind like back-ldap? 1434 */ 1435 rc = 1; 1436 break; 1437 1438 case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: { 1439 BerVarray *bvp; 1440 int i; 1441 struct berval bv = BER_BVNULL; 1442 char buf[SLAP_TEXT_BUFLEN]; 1443 1444 bvp = &mt->mt_idassert_authz; 1445 if ( *bvp == NULL ) { 1446 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) 1447 { 1448 BER_BVSTR( &bv, "*" ); 1449 value_add_one( &c->rvalue_vals, &bv ); 1450 1451 } else { 1452 rc = 1; 1453 } 1454 break; 1455 } 1456 1457 for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) { 1458 char *ptr; 1459 int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i ); 1460 bv.bv_len = ((*bvp)[ i ]).bv_len + len; 1461 bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 ); 1462 ptr = bv.bv_val; 1463 ptr = lutil_strcopy( ptr, buf ); 1464 ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len ); 1465 value_add_one( &c->rvalue_vals, &bv ); 1466 } 1467 if ( bv.bv_val ) { 1468 ber_memfree( bv.bv_val ); 1469 } 1470 break; 1471 } 1472 1473 case LDAP_BACK_CFG_IDASSERT_BIND: { 1474 int i; 1475 struct berval bc = BER_BVNULL; 1476 char *ptr; 1477 1478 if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) { 1479 return 1; 1480 } else { 1481 ber_len_t len; 1482 1483 switch ( mt->mt_idassert_mode ) { 1484 case LDAP_BACK_IDASSERT_OTHERID: 1485 case LDAP_BACK_IDASSERT_OTHERDN: 1486 break; 1487 1488 default: { 1489 struct berval mode = BER_BVNULL; 1490 1491 enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode ); 1492 if ( BER_BVISNULL( &mode ) ) { 1493 /* there's something wrong... */ 1494 assert( 0 ); 1495 rc = 1; 1496 1497 } else { 1498 bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len; 1499 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 1500 1501 ptr = lutil_strcopy( bv.bv_val, "mode=" ); 1502 ptr = lutil_strcopy( ptr, mode.bv_val ); 1503 } 1504 break; 1505 } 1506 } 1507 1508 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) { 1509 len = bv.bv_len + STRLENOF( "authz=native" ); 1510 1511 if ( !BER_BVISEMPTY( &bv ) ) { 1512 len += STRLENOF( " " ); 1513 } 1514 1515 bv.bv_val = ch_realloc( bv.bv_val, len + 1 ); 1516 1517 ptr = &bv.bv_val[ bv.bv_len ]; 1518 1519 if ( !BER_BVISEMPTY( &bv ) ) { 1520 ptr = lutil_strcopy( ptr, " " ); 1521 } 1522 1523 (void)lutil_strcopy( ptr, "authz=native" ); 1524 } 1525 1526 len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" ); 1527 /* flags */ 1528 if ( !BER_BVISEMPTY( &bv ) ) { 1529 len += STRLENOF( " " ); 1530 } 1531 1532 bv.bv_val = ch_realloc( bv.bv_val, len + 1 ); 1533 1534 ptr = &bv.bv_val[ bv.bv_len ]; 1535 1536 if ( !BER_BVISEMPTY( &bv ) ) { 1537 ptr = lutil_strcopy( ptr, " " ); 1538 } 1539 1540 ptr = lutil_strcopy( ptr, "flags=" ); 1541 1542 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { 1543 ptr = lutil_strcopy( ptr, "prescriptive" ); 1544 } else { 1545 ptr = lutil_strcopy( ptr, "non-prescriptive" ); 1546 } 1547 1548 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) { 1549 ptr = lutil_strcopy( ptr, ",override" ); 1550 } 1551 1552 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) { 1553 ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" ); 1554 1555 } else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) { 1556 ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" ); 1557 } 1558 1559 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) { 1560 ptr = lutil_strcopy( ptr, ",proxy-authz-critical" ); 1561 1562 } else { 1563 ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" ); 1564 } 1565 1566 #ifdef SLAP_AUTH_DN 1567 switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) { 1568 case LDAP_BACK_AUTH_DN_AUTHZID: 1569 ptr = lutil_strcopy( ptr, ",dn-authzid" ); 1570 break; 1571 1572 case LDAP_BACK_AUTH_DN_WHOAMI: 1573 ptr = lutil_strcopy( ptr, ",dn-whoami" ); 1574 break; 1575 1576 default: 1577 #if 0 /* implicit */ 1578 ptr = lutil_strcopy( ptr, ",dn-none" ); 1579 #endif 1580 break; 1581 } 1582 #endif 1583 1584 bv.bv_len = ( ptr - bv.bv_val ); 1585 /* end-of-flags */ 1586 } 1587 1588 bindconf_unparse( &mt->mt_idassert.si_bc, &bc ); 1589 1590 if ( !BER_BVISNULL( &bv ) ) { 1591 ber_len_t len = bv.bv_len + bc.bv_len; 1592 1593 bv.bv_val = ch_realloc( bv.bv_val, len + 1 ); 1594 1595 assert( bc.bv_val[ 0 ] == ' ' ); 1596 1597 ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val ); 1598 free( bc.bv_val ); 1599 bv.bv_len = ptr - bv.bv_val; 1600 1601 } else { 1602 for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ ) 1603 /* count spaces */ ; 1604 1605 if ( i ) { 1606 bc.bv_len -= i; 1607 AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 ); 1608 } 1609 1610 bv = bc; 1611 } 1612 1613 ber_bvarray_add( &c->rvalue_vals, &bv ); 1614 1615 break; 1616 } 1617 1618 case LDAP_BACK_CFG_SUFFIXM: /* unused */ 1619 case LDAP_BACK_CFG_REWRITE: 1620 if ( mt->mt_rwmap.rwm_bva_rewrite == NULL ) { 1621 rc = 1; 1622 } else { 1623 rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_rewrite, &c->rvalue_vals ); 1624 } 1625 break; 1626 1627 case LDAP_BACK_CFG_MAP: 1628 if ( mt->mt_rwmap.rwm_bva_map == NULL ) { 1629 rc = 1; 1630 } else { 1631 rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_map, &c->rvalue_vals ); 1632 } 1633 break; 1634 1635 case LDAP_BACK_CFG_SUBTREE_EX: 1636 case LDAP_BACK_CFG_SUBTREE_IN: 1637 rc = meta_subtree_unparse( c, mt ); 1638 break; 1639 1640 case LDAP_BACK_CFG_FILTER: 1641 if ( mt->mt_filter == NULL ) { 1642 rc = 1; 1643 } else { 1644 metafilter_t *mf; 1645 for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) 1646 value_add_one( &c->rvalue_vals, &mf->mf_regex_pattern ); 1647 } 1648 break; 1649 1650 /* replaced by idassert */ 1651 case LDAP_BACK_CFG_PSEUDOROOTDN: 1652 case LDAP_BACK_CFG_PSEUDOROOTPW: 1653 rc = 1; 1654 break; 1655 1656 case LDAP_BACK_CFG_KEEPALIVE: { 1657 struct berval bv; 1658 char buf[AC_LINE_MAX]; 1659 bv.bv_len = AC_LINE_MAX; 1660 bv.bv_val = &buf[0]; 1661 slap_keepalive_parse(&bv, &mt->mt_tls.sb_keepalive, 0, 0, 1); 1662 value_add_one( &c->rvalue_vals, &bv ); 1663 break; 1664 } 1665 1666 default: 1667 rc = 1; 1668 } 1669 return rc; 1670 } else if ( c->op == LDAP_MOD_DELETE ) { 1671 switch( c->type ) { 1672 /* Base attrs */ 1673 case LDAP_BACK_CFG_CONN_TTL: 1674 mi->mi_conn_ttl = 0; 1675 break; 1676 1677 case LDAP_BACK_CFG_DNCACHE_TTL: 1678 mi->mi_cache.ttl = META_DNCACHE_DISABLED; 1679 break; 1680 1681 case LDAP_BACK_CFG_IDLE_TIMEOUT: 1682 mi->mi_idle_timeout = 0; 1683 break; 1684 1685 case LDAP_BACK_CFG_ONERR: 1686 mi->mi_flags &= ~META_BACK_F_ONERR_MASK; 1687 break; 1688 1689 case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER: 1690 mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND; 1691 break; 1692 1693 case LDAP_BACK_CFG_SINGLECONN: 1694 mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN; 1695 break; 1696 1697 case LDAP_BACK_CFG_USETEMP: 1698 mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES; 1699 break; 1700 1701 case LDAP_BACK_CFG_CONNPOOLMAX: 1702 mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN; 1703 break; 1704 1705 /* common attrs */ 1706 case LDAP_BACK_CFG_BIND_TIMEOUT: 1707 mc->mc_bind_timeout.tv_sec = 0; 1708 mc->mc_bind_timeout.tv_usec = 0; 1709 break; 1710 1711 case LDAP_BACK_CFG_CANCEL: 1712 mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2; 1713 break; 1714 1715 case LDAP_BACK_CFG_CHASE: 1716 mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS; 1717 break; 1718 1719 #ifdef SLAPD_META_CLIENT_PR 1720 case LDAP_BACK_CFG_CLIENT_PR: 1721 mc->mc_ps = META_CLIENT_PR_DISABLE; 1722 break; 1723 #endif /* SLAPD_META_CLIENT_PR */ 1724 1725 case LDAP_BACK_CFG_DEFAULT_T: 1726 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE; 1727 break; 1728 1729 case LDAP_BACK_CFG_NETWORK_TIMEOUT: 1730 mc->mc_network_timeout = 0; 1731 break; 1732 1733 case LDAP_BACK_CFG_NOREFS: 1734 mc->mc_flags &= ~LDAP_BACK_F_NOREFS; 1735 break; 1736 1737 case LDAP_BACK_CFG_NOUNDEFFILTER: 1738 mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER; 1739 break; 1740 1741 case LDAP_BACK_CFG_NRETRIES: 1742 mc->mc_nretries = META_RETRY_DEFAULT; 1743 break; 1744 1745 case LDAP_BACK_CFG_QUARANTINE: 1746 if ( META_BACK_CMN_QUARANTINE( mc )) { 1747 mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine ); 1748 mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE; 1749 if ( mc == &mt->mt_mc ) { 1750 ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex ); 1751 mt->mt_isquarantined = 0; 1752 } 1753 } 1754 break; 1755 1756 case LDAP_BACK_CFG_REBIND: 1757 mc->mc_flags &= ~LDAP_BACK_F_SAVECRED; 1758 break; 1759 1760 case LDAP_BACK_CFG_TIMEOUT: 1761 for ( i = 0; i < SLAP_OP_LAST; i++ ) { 1762 mc->mc_timeout[ i ] = 0; 1763 } 1764 break; 1765 1766 case LDAP_BACK_CFG_VERSION: 1767 mc->mc_version = 0; 1768 break; 1769 1770 #ifdef SLAP_CONTROL_X_SESSION_TRACKING 1771 case LDAP_BACK_CFG_ST_REQUEST: 1772 mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST; 1773 break; 1774 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */ 1775 1776 case LDAP_BACK_CFG_T_F: 1777 mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2; 1778 break; 1779 1780 case LDAP_BACK_CFG_TLS: 1781 mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK; 1782 if ( mt ) 1783 bindconf_free( &mt->mt_tls ); 1784 break; 1785 1786 /* target attrs */ 1787 case LDAP_BACK_CFG_URI: 1788 if ( mt->mt_uri ) { 1789 ch_free( mt->mt_uri ); 1790 mt->mt_uri = NULL; 1791 } 1792 /* FIXME: should have a way to close all cached 1793 * connections associated with this target. 1794 */ 1795 break; 1796 1797 case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: { 1798 BerVarray *bvp; 1799 1800 bvp = &mt->mt_idassert_authz; 1801 if ( c->valx < 0 ) { 1802 if ( *bvp != NULL ) { 1803 ber_bvarray_free( *bvp ); 1804 *bvp = NULL; 1805 } 1806 1807 } else { 1808 if ( *bvp == NULL ) { 1809 rc = 1; 1810 break; 1811 } 1812 1813 for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) 1814 ; 1815 1816 if ( i >= c->valx ) { 1817 rc = 1; 1818 break; 1819 } 1820 ber_memfree( ((*bvp)[ c->valx ]).bv_val ); 1821 for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) { 1822 (*bvp)[ i ] = (*bvp)[ i + 1 ]; 1823 } 1824 BER_BVZERO( &((*bvp)[ i ]) ); 1825 } 1826 } break; 1827 1828 case LDAP_BACK_CFG_IDASSERT_BIND: 1829 bindconf_free( &mt->mt_idassert.si_bc ); 1830 memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) ); 1831 break; 1832 1833 case LDAP_BACK_CFG_SUFFIXM: /* unused */ 1834 case LDAP_BACK_CFG_REWRITE: 1835 { 1836 if ( c->valx >= 0 ) { 1837 int i; 1838 1839 for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ ); 1840 1841 if ( c->valx >= i ) { 1842 rc = 1; 1843 break; 1844 } 1845 1846 ber_memfree( mt->mt_rwmap.rwm_bva_rewrite[ c->valx ].bv_val ); 1847 for ( i = c->valx; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ] ); i++ ) 1848 { 1849 mt->mt_rwmap.rwm_bva_rewrite[ i ] = mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ]; 1850 } 1851 BER_BVZERO( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); 1852 1853 rewrite_info_delete( &mt->mt_rwmap.rwm_rw ); 1854 assert( mt->mt_rwmap.rwm_rw == NULL ); 1855 1856 rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw ); 1857 1858 for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ ) 1859 { 1860 ConfigArgs ca = { 0 }; 1861 1862 ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val; 1863 init_config_argv( &ca ); 1864 config_parse_ldif( &ca ); 1865 1866 if ( !strcasecmp( ca.argv[0], "suffixmassage" )) { 1867 rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt ); 1868 } else { 1869 rc = rewrite_parse( mt->mt_rwmap.rwm_rw, 1870 c->fname, c->lineno, ca.argc, ca.argv ); 1871 } 1872 1873 1874 ch_free( ca.tline ); 1875 ch_free( ca.argv ); 1876 1877 assert( rc == 0 ); 1878 } 1879 1880 } else if ( mt->mt_rwmap.rwm_rw != NULL ) { 1881 if ( mt->mt_rwmap.rwm_bva_rewrite ) { 1882 ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite ); 1883 mt->mt_rwmap.rwm_bva_rewrite = NULL; 1884 } 1885 if ( mt->mt_rwmap.rwm_rw ) 1886 rewrite_info_delete( &mt->mt_rwmap.rwm_rw ); 1887 1888 meta_rwi_init( &mt->mt_rwmap.rwm_rw ); 1889 } 1890 } 1891 break; 1892 1893 case LDAP_BACK_CFG_MAP: 1894 if ( mt->mt_rwmap.rwm_bva_map ) { 1895 ber_bvarray_free( mt->mt_rwmap.rwm_bva_map ); 1896 mt->mt_rwmap.rwm_bva_map = NULL; 1897 } 1898 meta_back_map_free( &mt->mt_rwmap.rwm_oc ); 1899 meta_back_map_free( &mt->mt_rwmap.rwm_at ); 1900 mt->mt_rwmap.rwm_oc.drop_missing = 0; 1901 mt->mt_rwmap.rwm_at.drop_missing = 0; 1902 break; 1903 1904 case LDAP_BACK_CFG_SUBTREE_EX: 1905 case LDAP_BACK_CFG_SUBTREE_IN: 1906 /* can only be one of exclude or include */ 1907 if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) { 1908 rc = 1; 1909 break; 1910 } 1911 if ( c->valx < 0 ) { 1912 meta_subtree_destroy( mt->mt_subtree ); 1913 mt->mt_subtree = NULL; 1914 } else { 1915 metasubtree_t *ms, **mprev; 1916 for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) { 1917 if ( i == c->valx ) { 1918 *mprev = ms->ms_next; 1919 meta_subtree_free( ms ); 1920 break; 1921 } 1922 i++; 1923 mprev = &ms->ms_next; 1924 } 1925 if ( i != c->valx ) 1926 rc = 1; 1927 } 1928 break; 1929 1930 case LDAP_BACK_CFG_FILTER: 1931 if ( c->valx < 0 ) { 1932 meta_filter_destroy( mt->mt_filter ); 1933 mt->mt_filter = NULL; 1934 } else { 1935 metafilter_t *mf, **mprev; 1936 for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) { 1937 if ( i == c->valx ) { 1938 *mprev = mf->mf_next; 1939 meta_filter_free( mf ); 1940 break; 1941 } 1942 i++; 1943 mprev = &mf->mf_next; 1944 } 1945 if ( i != c->valx ) 1946 rc = 1; 1947 } 1948 break; 1949 1950 case LDAP_BACK_CFG_KEEPALIVE: 1951 mt->mt_tls.sb_keepalive.sk_idle = 0; 1952 mt->mt_tls.sb_keepalive.sk_probes = 0; 1953 mt->mt_tls.sb_keepalive.sk_interval = 0; 1954 break; 1955 1956 default: 1957 rc = 1; 1958 break; 1959 } 1960 1961 return rc; 1962 } 1963 1964 if ( c->op == SLAP_CONFIG_ADD ) { 1965 if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) { 1966 /* exclude CFG_URI from this check */ 1967 if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) { 1968 if ( !mi->mi_ntargets ) { 1969 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1970 "need \"uri\" directive first" ); 1971 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 1972 return 1; 1973 } 1974 } 1975 if ( mi->mi_ntargets ) { 1976 mt = mi->mi_targets[ mi->mi_ntargets-1 ]; 1977 mc = &mt->mt_mc; 1978 } else { 1979 mt = NULL; 1980 mc = &mi->mi_mc; 1981 } 1982 } 1983 } else { 1984 if ( c->table == Cft_Database ) { 1985 mt = NULL; 1986 mc = &mi->mi_mc; 1987 } else { 1988 mt = c->ca_private; 1989 if ( mt ) 1990 mc = &mt->mt_mc; 1991 else 1992 mc = NULL; 1993 } 1994 } 1995 1996 switch( c->type ) { 1997 case LDAP_BACK_CFG_URI: { 1998 LDAPURLDesc *ludp; 1999 struct berval dn; 2000 int j; 2001 2002 char **uris = NULL; 2003 2004 if ( c->be->be_nsuffix == NULL ) { 2005 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2006 "the suffix must be defined before any target" ); 2007 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2008 return 1; 2009 } 2010 2011 i = mi->mi_ntargets++; 2012 2013 mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets, 2014 sizeof( metatarget_t * ) * mi->mi_ntargets ); 2015 if ( mi->mi_targets == NULL ) { 2016 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2017 "out of memory while storing server name" 2018 " in \"%s <protocol>://<server>[:port]/<naming context>\"", 2019 c->argv[0] ); 2020 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2021 return 1; 2022 } 2023 2024 if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) { 2025 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2026 "unable to init server" 2027 " in \"%s <protocol>://<server>[:port]/<naming context>\"", 2028 c->argv[0] ); 2029 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2030 return 1; 2031 } 2032 2033 mt = mi->mi_targets[ i ]; 2034 2035 mt->mt_rebind_f = mi->mi_rebind_f; 2036 mt->mt_urllist_f = mi->mi_urllist_f; 2037 mt->mt_urllist_p = mt; 2038 2039 if ( META_BACK_QUARANTINE( mi ) ) { 2040 ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex ); 2041 } 2042 mt->mt_mc = mi->mi_mc; 2043 2044 for ( j = 1; j < c->argc; j++ ) { 2045 char **tmpuris = ldap_str2charray( c->argv[ j ], "\t" ); 2046 2047 if ( tmpuris == NULL ) { 2048 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2049 "unable to parse URIs #%d" 2050 " in \"%s <protocol>://<server>[:port]/<naming context>\"", 2051 j-1, c->argv[0] ); 2052 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2053 return 1; 2054 } 2055 2056 if ( j == 1 ) { 2057 uris = tmpuris; 2058 2059 } else { 2060 ldap_charray_merge( &uris, tmpuris ); 2061 ldap_charray_free( tmpuris ); 2062 } 2063 } 2064 2065 for ( j = 0; uris[ j ] != NULL; j++ ) { 2066 char *tmpuri = NULL; 2067 2068 /* 2069 * uri MUST be legal! 2070 */ 2071 if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t", 2072 LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS 2073 || ludp->lud_next != NULL ) 2074 { 2075 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2076 "unable to parse URI #%d" 2077 " in \"%s <protocol>://<server>[:port]/<naming context>\"", 2078 j-1, c->argv[0] ); 2079 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2080 ldap_charray_free( uris ); 2081 return 1; 2082 } 2083 2084 if ( j == 0 ) { 2085 2086 /* 2087 * uri MUST have the <dn> part! 2088 */ 2089 if ( ludp->lud_dn == NULL ) { 2090 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2091 "missing <naming context> " 2092 " in \"%s <protocol>://<server>[:port]/<naming context>\"", 2093 c->argv[0] ); 2094 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2095 ldap_free_urllist( ludp ); 2096 ldap_charray_free( uris ); 2097 return 1; 2098 } 2099 2100 /* 2101 * copies and stores uri and suffix 2102 */ 2103 ber_str2bv( ludp->lud_dn, 0, 0, &dn ); 2104 rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix, 2105 &mt->mt_nsuffix, NULL ); 2106 if ( rc != LDAP_SUCCESS ) { 2107 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2108 "target DN is invalid \"%s\"", 2109 c->argv[1] ); 2110 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2111 ldap_free_urllist( ludp ); 2112 ldap_charray_free( uris ); 2113 return( 1 ); 2114 } 2115 2116 ludp->lud_dn[ 0 ] = '\0'; 2117 2118 switch ( ludp->lud_scope ) { 2119 case LDAP_SCOPE_DEFAULT: 2120 mt->mt_scope = LDAP_SCOPE_SUBTREE; 2121 break; 2122 2123 case LDAP_SCOPE_SUBTREE: 2124 case LDAP_SCOPE_SUBORDINATE: 2125 mt->mt_scope = ludp->lud_scope; 2126 break; 2127 2128 default: 2129 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2130 "invalid scope for target \"%s\"", 2131 c->argv[1] ); 2132 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2133 ldap_free_urllist( ludp ); 2134 ldap_charray_free( uris ); 2135 return( 1 ); 2136 } 2137 2138 } else { 2139 /* check all, to apply the scope check on the first one */ 2140 if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) { 2141 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2142 "multiple URIs must have no DN part" ); 2143 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2144 ldap_free_urllist( ludp ); 2145 ldap_charray_free( uris ); 2146 return( 1 ); 2147 2148 } 2149 } 2150 2151 tmpuri = ldap_url_list2urls( ludp ); 2152 ldap_free_urllist( ludp ); 2153 if ( tmpuri == NULL ) { 2154 snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" ); 2155 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2156 ldap_charray_free( uris ); 2157 return( 1 ); 2158 } 2159 ldap_memfree( uris[ j ] ); 2160 uris[ j ] = tmpuri; 2161 } 2162 2163 mt->mt_uri = ldap_charray2str( uris, " " ); 2164 ldap_charray_free( uris ); 2165 if ( mt->mt_uri == NULL) { 2166 snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" ); 2167 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2168 return( 1 ); 2169 } 2170 2171 /* 2172 * uri MUST be a branch of suffix! 2173 */ 2174 for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) { 2175 if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) { 2176 break; 2177 } 2178 } 2179 2180 if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) { 2181 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2182 "<naming context> of URI must be within the naming context of this database." ); 2183 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2184 return 1; 2185 } 2186 c->ca_private = mt; 2187 c->cleanup = meta_cf_cleanup; 2188 } break; 2189 case LDAP_BACK_CFG_SUBTREE_EX: 2190 case LDAP_BACK_CFG_SUBTREE_IN: 2191 /* subtree-exclude */ 2192 if ( meta_subtree_config( mt, c )) { 2193 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2194 return 1; 2195 } 2196 break; 2197 2198 case LDAP_BACK_CFG_FILTER: { 2199 metafilter_t *mf, **m2; 2200 mf = ch_calloc( 1, sizeof( metafilter_t )); 2201 rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED ); 2202 if ( rc ) { 2203 char regerr[ SLAP_TEXT_BUFLEN ]; 2204 regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) ); 2205 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2206 "regular expression \"%s\" bad because of %s", 2207 c->argv[1], regerr ); 2208 ch_free( mf ); 2209 return 1; 2210 } 2211 ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern ); 2212 for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next ) 2213 ; 2214 *m2 = mf; 2215 } break; 2216 2217 case LDAP_BACK_CFG_DEFAULT_T: 2218 /* default target directive */ 2219 i = mi->mi_ntargets - 1; 2220 2221 if ( c->argc == 1 ) { 2222 if ( i < 0 ) { 2223 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2224 "\"%s\" alone must be inside a \"uri\" directive", 2225 c->argv[0] ); 2226 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2227 return 1; 2228 } 2229 mi->mi_defaulttarget = i; 2230 2231 } else { 2232 if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) { 2233 if ( i >= 0 ) { 2234 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2235 "\"%s none\" should go before uri definitions", 2236 c->argv[0] ); 2237 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2238 } 2239 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE; 2240 2241 } else { 2242 2243 if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0 2244 || mi->mi_defaulttarget < 0 2245 || mi->mi_defaulttarget >= i - 1 ) 2246 { 2247 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2248 "illegal target number %d", 2249 mi->mi_defaulttarget ); 2250 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2251 return 1; 2252 } 2253 } 2254 } 2255 break; 2256 2257 case LDAP_BACK_CFG_DNCACHE_TTL: 2258 /* ttl of dn cache */ 2259 if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) { 2260 mi->mi_cache.ttl = META_DNCACHE_FOREVER; 2261 2262 } else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) { 2263 mi->mi_cache.ttl = META_DNCACHE_DISABLED; 2264 2265 } else { 2266 unsigned long t; 2267 2268 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) { 2269 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2270 "unable to parse dncache ttl \"%s\"", 2271 c->argv[ 1 ] ); 2272 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2273 return 1; 2274 } 2275 mi->mi_cache.ttl = (time_t)t; 2276 } 2277 break; 2278 2279 case LDAP_BACK_CFG_NETWORK_TIMEOUT: { 2280 /* network timeout when connecting to ldap servers */ 2281 unsigned long t; 2282 2283 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) { 2284 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2285 "unable to parse network timeout \"%s\"", 2286 c->argv[ 1 ] ); 2287 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2288 return 1; 2289 } 2290 mc->mc_network_timeout = (time_t)t; 2291 } break; 2292 2293 case LDAP_BACK_CFG_IDLE_TIMEOUT: { 2294 /* idle timeout when connecting to ldap servers */ 2295 unsigned long t; 2296 2297 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) { 2298 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2299 "unable to parse idle timeout \"%s\"", 2300 c->argv[ 1 ] ); 2301 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2302 return 1; 2303 2304 } 2305 mi->mi_idle_timeout = (time_t)t; 2306 } break; 2307 2308 case LDAP_BACK_CFG_CONN_TTL: { 2309 /* conn ttl */ 2310 unsigned long t; 2311 2312 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) { 2313 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2314 "unable to parse conn ttl \"%s\"", 2315 c->argv[ 1 ] ); 2316 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2317 return 1; 2318 2319 } 2320 mi->mi_conn_ttl = (time_t)t; 2321 } break; 2322 2323 case LDAP_BACK_CFG_BIND_TIMEOUT: 2324 /* bind timeout when connecting to ldap servers */ 2325 mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000; 2326 mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000; 2327 break; 2328 2329 case LDAP_BACK_CFG_ACL_AUTHCDN: 2330 /* name to use for meta_back_group */ 2331 if ( strcasecmp( c->argv[ 0 ], "binddn" ) == 0 ) { 2332 Debug( LDAP_DEBUG_ANY, "%s: " 2333 "\"binddn\" statement is deprecated; " 2334 "use \"acl-authcDN\" instead\n", 2335 c->log, 0, 0 ); 2336 /* FIXME: some day we'll need to throw an error */ 2337 } 2338 2339 ber_memfree_x( c->value_dn.bv_val, NULL ); 2340 mt->mt_binddn = c->value_ndn; 2341 BER_BVZERO( &c->value_dn ); 2342 BER_BVZERO( &c->value_ndn ); 2343 break; 2344 2345 case LDAP_BACK_CFG_ACL_PASSWD: 2346 /* password to use for meta_back_group */ 2347 if ( strcasecmp( c->argv[ 0 ], "bindpw" ) == 0 ) { 2348 Debug( LDAP_DEBUG_ANY, "%s " 2349 "\"bindpw\" statement is deprecated; " 2350 "use \"acl-passwd\" instead\n", 2351 c->log, 0, 0 ); 2352 /* FIXME: some day we'll need to throw an error */ 2353 } 2354 2355 ber_str2bv( c->argv[ 1 ], 0L, 1, &mt->mt_bindpw ); 2356 break; 2357 2358 case LDAP_BACK_CFG_REBIND: 2359 /* save bind creds for referral rebinds? */ 2360 if ( c->argc == 1 || c->value_int ) { 2361 mc->mc_flags |= LDAP_BACK_F_SAVECRED; 2362 } else { 2363 mc->mc_flags &= ~LDAP_BACK_F_SAVECRED; 2364 } 2365 break; 2366 2367 case LDAP_BACK_CFG_CHASE: 2368 if ( c->argc == 1 || c->value_int ) { 2369 mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS; 2370 } else { 2371 mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS; 2372 } 2373 break; 2374 2375 case LDAP_BACK_CFG_TLS: 2376 i = verb_to_mask( c->argv[1], tls_mode ); 2377 if ( BER_BVISNULL( &tls_mode[i].word ) ) { 2378 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2379 "%s unknown argument \"%s\"", 2380 c->argv[0], c->argv[1] ); 2381 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2382 return 1; 2383 } 2384 mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK; 2385 mc->mc_flags |= tls_mode[i].mask; 2386 2387 if ( c->argc > 2 ) { 2388 if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) { 2389 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2390 "need \"uri\" directive first" ); 2391 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2392 return 1; 2393 } 2394 2395 for ( i = 2; i < c->argc; i++ ) { 2396 if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls )) 2397 return 1; 2398 } 2399 bindconf_tls_defaults( &mt->mt_tls ); 2400 } 2401 break; 2402 2403 case LDAP_BACK_CFG_T_F: 2404 i = verb_to_mask( c->argv[1], t_f_mode ); 2405 if ( BER_BVISNULL( &t_f_mode[i].word ) ) { 2406 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2407 "%s unknown argument \"%s\"", 2408 c->argv[0], c->argv[1] ); 2409 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2410 return 1; 2411 } 2412 mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2; 2413 mc->mc_flags |= t_f_mode[i].mask; 2414 break; 2415 2416 case LDAP_BACK_CFG_ONERR: 2417 /* onerr? */ 2418 i = verb_to_mask( c->argv[1], onerr_mode ); 2419 if ( BER_BVISNULL( &onerr_mode[i].word ) ) { 2420 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2421 "%s unknown argument \"%s\"", 2422 c->argv[0], c->argv[1] ); 2423 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2424 return 1; 2425 } 2426 mi->mi_flags &= ~META_BACK_F_ONERR_MASK; 2427 mi->mi_flags |= onerr_mode[i].mask; 2428 break; 2429 2430 case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER: 2431 /* bind-defer? */ 2432 if ( c->argc == 1 || c->value_int ) { 2433 mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND; 2434 } else { 2435 mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND; 2436 } 2437 break; 2438 2439 case LDAP_BACK_CFG_SINGLECONN: 2440 /* single-conn? */ 2441 if ( mi->mi_ntargets > 0 ) { 2442 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2443 "\"%s\" must appear before target definitions", 2444 c->argv[0] ); 2445 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2446 return( 1 ); 2447 } 2448 if ( c->value_int ) { 2449 mi->mi_flags |= LDAP_BACK_F_SINGLECONN; 2450 } else { 2451 mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN; 2452 } 2453 break; 2454 2455 case LDAP_BACK_CFG_USETEMP: 2456 /* use-temporaries? */ 2457 if ( mi->mi_ntargets > 0 ) { 2458 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2459 "\"%s\" must appear before target definitions", 2460 c->argv[0] ); 2461 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2462 return( 1 ); 2463 } 2464 if ( c->value_int ) { 2465 mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES; 2466 } else { 2467 mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES; 2468 } 2469 break; 2470 2471 case LDAP_BACK_CFG_CONNPOOLMAX: 2472 /* privileged connections pool max size ? */ 2473 if ( mi->mi_ntargets > 0 ) { 2474 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2475 "\"%s\" must appear before target definitions", 2476 c->argv[0] ); 2477 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2478 return( 1 ); 2479 } 2480 2481 if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN 2482 || c->value_int > LDAP_BACK_CONN_PRIV_MAX ) 2483 { 2484 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2485 "invalid max size " "of privileged " 2486 "connections pool \"%s\" " 2487 "in \"conn-pool-max <n> " 2488 "(must be between %d and %d)\"", 2489 c->argv[ 1 ], 2490 LDAP_BACK_CONN_PRIV_MIN, 2491 LDAP_BACK_CONN_PRIV_MAX ); 2492 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2493 return 1; 2494 } 2495 mi->mi_conn_priv_max = c->value_int; 2496 break; 2497 2498 case LDAP_BACK_CFG_CANCEL: 2499 i = verb_to_mask( c->argv[1], cancel_mode ); 2500 if ( BER_BVISNULL( &cancel_mode[i].word ) ) { 2501 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2502 "%s unknown argument \"%s\"", 2503 c->argv[0], c->argv[1] ); 2504 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2505 return 1; 2506 } 2507 mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2; 2508 mc->mc_flags |= cancel_mode[i].mask; 2509 break; 2510 2511 case LDAP_BACK_CFG_TIMEOUT: 2512 for ( i = 1; i < c->argc; i++ ) { 2513 if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) { 2514 int j; 2515 unsigned u; 2516 2517 if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) { 2518 snprintf( c->cr_msg, sizeof( c->cr_msg), 2519 "unable to parse timeout \"%s\"", 2520 c->argv[ i ] ); 2521 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2522 return 1; 2523 } 2524 2525 for ( j = 0; j < SLAP_OP_LAST; j++ ) { 2526 mc->mc_timeout[ j ] = u; 2527 } 2528 2529 continue; 2530 } 2531 2532 if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) { 2533 snprintf( c->cr_msg, sizeof( c->cr_msg), 2534 "unable to parse timeout \"%s\"", 2535 c->argv[ i ] ); 2536 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2537 return 1; 2538 } 2539 } 2540 break; 2541 2542 case LDAP_BACK_CFG_PSEUDOROOTDN: 2543 /* name to use as pseudo-root dn */ 2544 /* 2545 * exact replacement: 2546 * 2547 2548 idassert-bind bindmethod=simple 2549 binddn=<pseudorootdn> 2550 credentials=<pseudorootpw> 2551 mode=none 2552 flags=non-prescriptive 2553 idassert-authzFrom "dn:<rootdn>" 2554 2555 * so that only when authc'd as <rootdn> the proxying occurs 2556 * rebinding as the <pseudorootdn> without proxyAuthz. 2557 */ 2558 2559 Debug( LDAP_DEBUG_ANY, 2560 "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; " 2561 "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n", 2562 c->log, 0, 0 ); 2563 2564 { 2565 char binddn[ SLAP_TEXT_BUFLEN ]; 2566 char *cargv[] = { 2567 "idassert-bind", 2568 "bindmethod=simple", 2569 NULL, 2570 "mode=none", 2571 "flags=non-prescriptive", 2572 NULL 2573 }; 2574 char **oargv; 2575 int oargc; 2576 int cargc = 5; 2577 int rc; 2578 2579 2580 if ( BER_BVISNULL( &c->be->be_rootndn ) ) { 2581 Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"rootdn\" must be defined first.\n", 2582 c->log, 0, 0 ); 2583 return 1; 2584 } 2585 2586 if ( sizeof( binddn ) <= (unsigned) snprintf( binddn, 2587 sizeof( binddn ), "binddn=%s", c->argv[ 1 ] )) 2588 { 2589 Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootdn\" too long.\n", 2590 c->log, 0, 0 ); 2591 return 1; 2592 } 2593 cargv[ 2 ] = binddn; 2594 2595 oargv = c->argv; 2596 oargc = c->argc; 2597 c->argv = cargv; 2598 c->argc = cargc; 2599 rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert ); 2600 c->argv = oargv; 2601 c->argc = oargc; 2602 if ( rc == 0 ) { 2603 struct berval bv; 2604 2605 if ( mt->mt_idassert_authz != NULL ) { 2606 Debug( LDAP_DEBUG_ANY, "%s: \"idassert-authzFrom\" already defined (discarded).\n", 2607 c->log, 0, 0 ); 2608 ber_bvarray_free( mt->mt_idassert_authz ); 2609 mt->mt_idassert_authz = NULL; 2610 } 2611 2612 assert( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ); 2613 2614 bv.bv_len = STRLENOF( "dn:" ) + c->be->be_rootndn.bv_len; 2615 bv.bv_val = ber_memalloc( bv.bv_len + 1 ); 2616 AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) ); 2617 AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], c->be->be_rootndn.bv_val, c->be->be_rootndn.bv_len + 1 ); 2618 2619 ber_bvarray_add( &mt->mt_idassert_authz, &bv ); 2620 } 2621 2622 return rc; 2623 } 2624 break; 2625 2626 case LDAP_BACK_CFG_PSEUDOROOTPW: 2627 /* password to use as pseudo-root */ 2628 Debug( LDAP_DEBUG_ANY, 2629 "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; " 2630 "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n", 2631 c->log, 0, 0 ); 2632 2633 if ( BER_BVISNULL( &mt->mt_idassert_authcDN ) ) { 2634 Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n", 2635 c->log, 0, 0 ); 2636 return 1; 2637 } 2638 2639 if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) { 2640 memset( mt->mt_idassert_passwd.bv_val, 0, 2641 mt->mt_idassert_passwd.bv_len ); 2642 ber_memfree( mt->mt_idassert_passwd.bv_val ); 2643 } 2644 ber_str2bv( c->argv[ 1 ], 0, 1, &mt->mt_idassert_passwd ); 2645 break; 2646 2647 case LDAP_BACK_CFG_IDASSERT_BIND: 2648 /* idassert-bind */ 2649 rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert ); 2650 break; 2651 2652 case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: 2653 /* idassert-authzFrom */ 2654 rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert ); 2655 break; 2656 2657 case LDAP_BACK_CFG_QUARANTINE: 2658 /* quarantine */ 2659 if ( META_BACK_CMN_QUARANTINE( mc ) ) 2660 { 2661 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2662 "quarantine already defined" ); 2663 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2664 return 1; 2665 } 2666 2667 if ( mt ) { 2668 mc->mc_quarantine.ri_interval = NULL; 2669 mc->mc_quarantine.ri_num = NULL; 2670 if ( !META_BACK_QUARANTINE( mi ) ) { 2671 ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex ); 2672 } 2673 } 2674 2675 if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) { 2676 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2677 return 1; 2678 } 2679 2680 mc->mc_flags |= LDAP_BACK_F_QUARANTINE; 2681 break; 2682 2683 #ifdef SLAP_CONTROL_X_SESSION_TRACKING 2684 case LDAP_BACK_CFG_ST_REQUEST: 2685 /* session tracking request */ 2686 if ( c->value_int ) { 2687 mc->mc_flags |= LDAP_BACK_F_ST_REQUEST; 2688 } else { 2689 mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST; 2690 } 2691 break; 2692 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */ 2693 2694 case LDAP_BACK_CFG_SUFFIXM: /* FALLTHRU */ 2695 case LDAP_BACK_CFG_REWRITE: { 2696 /* rewrite stuff ... */ 2697 ConfigArgs ca = { 0 }; 2698 char *line, **argv; 2699 struct rewrite_info *rwi; 2700 int cnt = 0, argc, ix = c->valx; 2701 2702 if ( mt->mt_rwmap.rwm_bva_rewrite ) { 2703 for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ ) 2704 /* count */ ; 2705 } 2706 2707 if ( ix >= cnt || ix < 0 ) { 2708 ix = cnt; 2709 } else { 2710 rwi = mt->mt_rwmap.rwm_rw; 2711 2712 mt->mt_rwmap.rwm_rw = NULL; 2713 rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw ); 2714 2715 /* re-parse all rewrite rules, up to the one 2716 * that needs to be added */ 2717 ca.be = c->be; 2718 ca.fname = c->fname; 2719 ca.lineno = c->lineno; 2720 for ( i = 0; i < ix; i++ ) { 2721 ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val; 2722 ca.argc = 0; 2723 config_fp_parse_line( &ca ); 2724 2725 if ( !strcasecmp( ca.argv[0], "suffixmassage" )) { 2726 rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt ); 2727 } else { 2728 rc = rewrite_parse( mt->mt_rwmap.rwm_rw, 2729 c->fname, c->lineno, ca.argc, ca.argv ); 2730 } 2731 assert( rc == 0 ); 2732 ch_free( ca.argv ); 2733 ch_free( ca.tline ); 2734 } 2735 } 2736 argc = c->argc; 2737 argv = c->argv; 2738 if ( c->op != SLAP_CONFIG_ADD ) { 2739 argc--; 2740 argv++; 2741 } 2742 /* add the new rule */ 2743 if ( !strcasecmp( argv[0], "suffixmassage" )) { 2744 rc = meta_suffixm_config( c, argc, argv, mt ); 2745 } else { 2746 rc = rewrite_parse( mt->mt_rwmap.rwm_rw, 2747 c->fname, c->lineno, argc, argv ); 2748 } 2749 if ( rc ) { 2750 if ( ix < cnt ) { 2751 rewrite_info_delete( &mt->mt_rwmap.rwm_rw ); 2752 mt->mt_rwmap.rwm_rw = rwi; 2753 } 2754 return 1; 2755 } 2756 if ( ix < cnt ) { 2757 for ( ; i < cnt; i++ ) { 2758 ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val; 2759 ca.argc = 0; 2760 config_fp_parse_line( &ca ); 2761 2762 if ( !strcasecmp( ca.argv[0], "suffixmassage" )) { 2763 rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt ); 2764 } else { 2765 rc = rewrite_parse( mt->mt_rwmap.rwm_rw, 2766 c->fname, c->lineno, ca.argc, argv ); 2767 } 2768 assert( rc == 0 ); 2769 ch_free( ca.argv ); 2770 ch_free( ca.tline ); 2771 } 2772 } 2773 2774 /* save the rule info */ 2775 line = ldap_charray2str( argv, "\" \"" ); 2776 if ( line != NULL ) { 2777 struct berval bv; 2778 int len = strlen( argv[ 0 ] ); 2779 2780 ber_str2bv( line, 0, 0, &bv ); 2781 AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ], 2782 bv.bv_len - ( len + 1 )); 2783 bv.bv_val[ bv.bv_len - 1] = '"'; 2784 ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv ); 2785 /* move it to the right slot */ 2786 if ( ix < cnt ) { 2787 for ( i=cnt; i>ix; i-- ) 2788 mt->mt_rwmap.rwm_bva_rewrite[i+1] = mt->mt_rwmap.rwm_bva_rewrite[i]; 2789 mt->mt_rwmap.rwm_bva_rewrite[i] = bv; 2790 2791 /* destroy old rules */ 2792 rewrite_info_delete( &rwi ); 2793 } 2794 } 2795 } break; 2796 2797 case LDAP_BACK_CFG_MAP: { 2798 /* objectclass/attribute mapping */ 2799 ConfigArgs ca = { 0 }; 2800 char *argv[5]; 2801 struct ldapmap rwm_oc; 2802 struct ldapmap rwm_at; 2803 int cnt = 0, ix = c->valx; 2804 2805 if ( mt->mt_rwmap.rwm_bva_map ) { 2806 for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ ) 2807 /* count */ ; 2808 } 2809 2810 if ( ix >= cnt || ix < 0 ) { 2811 ix = cnt; 2812 } else { 2813 rwm_oc = mt->mt_rwmap.rwm_oc; 2814 rwm_at = mt->mt_rwmap.rwm_at; 2815 2816 memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) ); 2817 memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) ); 2818 2819 /* re-parse all mappings, up to the one 2820 * that needs to be added */ 2821 argv[0] = c->argv[0]; 2822 ca.fname = c->fname; 2823 ca.lineno = c->lineno; 2824 for ( i = 0; i < ix; i++ ) { 2825 ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val; 2826 ca.argc = 0; 2827 config_fp_parse_line( &ca ); 2828 2829 argv[1] = ca.argv[0]; 2830 argv[2] = ca.argv[1]; 2831 argv[3] = ca.argv[2]; 2832 argv[4] = ca.argv[3]; 2833 ch_free( ca.argv ); 2834 ca.argv = argv; 2835 ca.argc++; 2836 rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc, 2837 &mt->mt_rwmap.rwm_at ); 2838 2839 ch_free( ca.tline ); 2840 ca.tline = NULL; 2841 ca.argv = NULL; 2842 2843 /* in case of failure, restore 2844 * the existing mapping */ 2845 if ( rc ) { 2846 goto map_fail; 2847 } 2848 } 2849 } 2850 /* add the new mapping */ 2851 rc = ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc, 2852 &mt->mt_rwmap.rwm_at ); 2853 if ( rc ) { 2854 goto map_fail; 2855 } 2856 2857 if ( ix < cnt ) { 2858 for ( ; i<cnt ; cnt++ ) { 2859 ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val; 2860 ca.argc = 0; 2861 config_fp_parse_line( &ca ); 2862 2863 argv[1] = ca.argv[0]; 2864 argv[2] = ca.argv[1]; 2865 argv[3] = ca.argv[2]; 2866 argv[4] = ca.argv[3]; 2867 2868 ch_free( ca.argv ); 2869 ca.argv = argv; 2870 ca.argc++; 2871 rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc, 2872 &mt->mt_rwmap.rwm_at ); 2873 2874 ch_free( ca.tline ); 2875 ca.tline = NULL; 2876 ca.argv = NULL; 2877 2878 /* in case of failure, restore 2879 * the existing mapping */ 2880 if ( rc ) { 2881 goto map_fail; 2882 } 2883 } 2884 } 2885 2886 /* save the map info */ 2887 argv[0] = ldap_charray2str( &c->argv[ 1 ], " " ); 2888 if ( argv[0] != NULL ) { 2889 struct berval bv; 2890 ber_str2bv( argv[0], 0, 0, &bv ); 2891 ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv ); 2892 /* move it to the right slot */ 2893 if ( ix < cnt ) { 2894 for ( i=cnt; i>ix; i-- ) 2895 mt->mt_rwmap.rwm_bva_map[i+1] = mt->mt_rwmap.rwm_bva_map[i]; 2896 mt->mt_rwmap.rwm_bva_map[i] = bv; 2897 2898 /* destroy old mapping */ 2899 meta_back_map_free( &rwm_oc ); 2900 meta_back_map_free( &rwm_at ); 2901 } 2902 } 2903 break; 2904 2905 map_fail:; 2906 if ( ix < cnt ) { 2907 meta_back_map_free( &mt->mt_rwmap.rwm_oc ); 2908 meta_back_map_free( &mt->mt_rwmap.rwm_at ); 2909 mt->mt_rwmap.rwm_oc = rwm_oc; 2910 mt->mt_rwmap.rwm_at = rwm_at; 2911 } 2912 } break; 2913 2914 case LDAP_BACK_CFG_NRETRIES: { 2915 int nretries = META_RETRY_UNDEFINED; 2916 2917 if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) { 2918 nretries = META_RETRY_FOREVER; 2919 2920 } else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) { 2921 nretries = META_RETRY_NEVER; 2922 2923 } else { 2924 if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) { 2925 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2926 "unable to parse nretries {never|forever|<retries>}: \"%s\"", 2927 c->argv[ 1 ] ); 2928 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2929 return 1; 2930 } 2931 } 2932 2933 mc->mc_nretries = nretries; 2934 } break; 2935 2936 case LDAP_BACK_CFG_VERSION: 2937 if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) { 2938 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2939 "unsupported protocol version \"%s\"", 2940 c->argv[ 1 ] ); 2941 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2942 return 1; 2943 } 2944 mc->mc_version = c->value_int; 2945 break; 2946 2947 case LDAP_BACK_CFG_NOREFS: 2948 /* do not return search references */ 2949 if ( c->value_int ) { 2950 mc->mc_flags |= LDAP_BACK_F_NOREFS; 2951 } else { 2952 mc->mc_flags &= ~LDAP_BACK_F_NOREFS; 2953 } 2954 break; 2955 2956 case LDAP_BACK_CFG_NOUNDEFFILTER: 2957 /* do not propagate undefined search filters */ 2958 if ( c->value_int ) { 2959 mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER; 2960 } else { 2961 mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER; 2962 } 2963 break; 2964 2965 #ifdef SLAPD_META_CLIENT_PR 2966 case LDAP_BACK_CFG_CLIENT_PR: 2967 if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) { 2968 mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED; 2969 2970 } else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) { 2971 mc->mc_ps = META_CLIENT_PR_DISABLE; 2972 2973 } else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) { 2974 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2975 "unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"", 2976 c->argv[ 1 ] ); 2977 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 2978 return( 1 ); 2979 } 2980 break; 2981 #endif /* SLAPD_META_CLIENT_PR */ 2982 2983 case LDAP_BACK_CFG_KEEPALIVE: 2984 slap_keepalive_parse( ber_bvstrdup(c->argv[1]), 2985 &mt->mt_tls.sb_keepalive, 0, 0, 0); 2986 break; 2987 2988 /* anything else */ 2989 default: 2990 return SLAP_CONF_UNKNOWN; 2991 } 2992 2993 return rc; 2994 } 2995 2996 int 2997 meta_back_init_cf( BackendInfo *bi ) 2998 { 2999 int rc; 3000 AttributeDescription *ad = NULL; 3001 const char *text; 3002 3003 /* Make sure we don't exceed the bits reserved for userland */ 3004 config_check_userland( LDAP_BACK_CFG_LAST ); 3005 3006 bi->bi_cf_ocs = metaocs; 3007 3008 rc = config_register_schema( metacfg, metaocs ); 3009 if ( rc ) { 3010 return rc; 3011 } 3012 3013 /* setup olcDbAclPasswd and olcDbIDAssertPasswd 3014 * to be base64-encoded when written in LDIF form; 3015 * basically, we don't care if it fails */ 3016 rc = slap_str2ad( "olcDbACLPasswd", &ad, &text ); 3017 if ( rc ) { 3018 Debug( LDAP_DEBUG_ANY, "config_back_initialize: " 3019 "warning, unable to get \"olcDbACLPasswd\" " 3020 "attribute description: %d: %s\n", 3021 rc, text, 0 ); 3022 } else { 3023 (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val, 3024 ad->ad_type->sat_oid ); 3025 } 3026 3027 ad = NULL; 3028 rc = slap_str2ad( "olcDbIDAssertPasswd", &ad, &text ); 3029 if ( rc ) { 3030 Debug( LDAP_DEBUG_ANY, "config_back_initialize: " 3031 "warning, unable to get \"olcDbIDAssertPasswd\" " 3032 "attribute description: %d: %s\n", 3033 rc, text, 0 ); 3034 } else { 3035 (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val, 3036 ad->ad_type->sat_oid ); 3037 } 3038 3039 return 0; 3040 } 3041 3042 static int 3043 ldap_back_map_config( 3044 ConfigArgs *c, 3045 struct ldapmap *oc_map, 3046 struct ldapmap *at_map ) 3047 { 3048 struct ldapmap *map; 3049 struct ldapmapping *mapping; 3050 char *src, *dst; 3051 int is_oc = 0; 3052 3053 if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) { 3054 map = oc_map; 3055 is_oc = 1; 3056 3057 } else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) { 3058 map = at_map; 3059 3060 } else { 3061 snprintf( c->cr_msg, sizeof(c->cr_msg), 3062 "%s unknown argument \"%s\"", 3063 c->argv[0], c->argv[1] ); 3064 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3065 return 1; 3066 } 3067 3068 if ( !is_oc && map->map == NULL ) { 3069 /* only init if required */ 3070 ldap_back_map_init( map, &mapping ); 3071 } 3072 3073 if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) { 3074 if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) { 3075 map->drop_missing = ( c->argc < 4 ); 3076 goto success_return; 3077 } 3078 src = dst = c->argv[ 3 ]; 3079 3080 } else if ( c->argc < 4 ) { 3081 src = ""; 3082 dst = c->argv[ 2 ]; 3083 3084 } else { 3085 src = c->argv[ 2 ]; 3086 dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] ); 3087 } 3088 3089 if ( ( map == at_map ) 3090 && ( strcasecmp( src, "objectclass" ) == 0 3091 || strcasecmp( dst, "objectclass" ) == 0 ) ) 3092 { 3093 snprintf( c->cr_msg, sizeof(c->cr_msg), 3094 "objectclass attribute cannot be mapped" ); 3095 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3096 return 1; 3097 } 3098 3099 mapping = (struct ldapmapping *)ch_calloc( 2, 3100 sizeof(struct ldapmapping) ); 3101 if ( mapping == NULL ) { 3102 snprintf( c->cr_msg, sizeof(c->cr_msg), 3103 "out of memory" ); 3104 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3105 return 1; 3106 } 3107 ber_str2bv( src, 0, 1, &mapping[ 0 ].src ); 3108 ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst ); 3109 mapping[ 1 ].src = mapping[ 0 ].dst; 3110 mapping[ 1 ].dst = mapping[ 0 ].src; 3111 3112 /* 3113 * schema check 3114 */ 3115 if ( is_oc ) { 3116 if ( src[ 0 ] != '\0' ) { 3117 if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) { 3118 Debug( LDAP_DEBUG_ANY, 3119 "warning, source objectClass '%s' should be defined in schema\n", 3120 c->log, src, 0 ); 3121 3122 /* 3123 * FIXME: this should become an err 3124 */ 3125 goto error_return; 3126 } 3127 } 3128 3129 if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) { 3130 Debug( LDAP_DEBUG_ANY, 3131 "warning, destination objectClass '%s' is not defined in schema\n", 3132 c->log, dst, 0 ); 3133 } 3134 } else { 3135 int rc; 3136 const char *text = NULL; 3137 AttributeDescription *ad = NULL; 3138 3139 if ( src[ 0 ] != '\0' ) { 3140 rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text ); 3141 if ( rc != LDAP_SUCCESS ) { 3142 Debug( LDAP_DEBUG_ANY, 3143 "warning, source attributeType '%s' should be defined in schema\n", 3144 c->log, src, 0 ); 3145 3146 /* 3147 * FIXME: this should become an err 3148 */ 3149 /* 3150 * we create a fake "proxied" ad 3151 * and add it here. 3152 */ 3153 3154 rc = slap_bv2undef_ad( &mapping[ 0 ].src, 3155 &ad, &text, SLAP_AD_PROXIED ); 3156 if ( rc != LDAP_SUCCESS ) { 3157 snprintf( c->cr_msg, sizeof( c->cr_msg ), 3158 "source attributeType \"%s\": %d (%s)", 3159 src, rc, text ? text : "" ); 3160 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3161 goto error_return; 3162 } 3163 } 3164 3165 ad = NULL; 3166 } 3167 3168 rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text ); 3169 if ( rc != LDAP_SUCCESS ) { 3170 Debug( LDAP_DEBUG_ANY, 3171 "warning, destination attributeType '%s' is not defined in schema\n", 3172 c->log, dst, 0 ); 3173 3174 /* 3175 * we create a fake "proxied" ad 3176 * and add it here. 3177 */ 3178 3179 rc = slap_bv2undef_ad( &mapping[ 0 ].dst, 3180 &ad, &text, SLAP_AD_PROXIED ); 3181 if ( rc != LDAP_SUCCESS ) { 3182 snprintf( c->cr_msg, sizeof( c->cr_msg ), 3183 "destination attributeType \"%s\": %d (%s)\n", 3184 dst, rc, text ? text : "" ); 3185 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3186 return 1; 3187 } 3188 } 3189 } 3190 3191 if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL) 3192 || avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL) 3193 { 3194 snprintf( c->cr_msg, sizeof( c->cr_msg ), 3195 "duplicate mapping found." ); 3196 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3197 goto error_return; 3198 } 3199 3200 if ( src[ 0 ] != '\0' ) { 3201 avl_insert( &map->map, (caddr_t)&mapping[ 0 ], 3202 mapping_cmp, mapping_dup ); 3203 } 3204 avl_insert( &map->remap, (caddr_t)&mapping[ 1 ], 3205 mapping_cmp, mapping_dup ); 3206 3207 success_return:; 3208 return 0; 3209 3210 error_return:; 3211 if ( mapping ) { 3212 ch_free( mapping[ 0 ].src.bv_val ); 3213 ch_free( mapping[ 0 ].dst.bv_val ); 3214 ch_free( mapping ); 3215 } 3216 3217 return 1; 3218 } 3219 3220 3221 #ifdef ENABLE_REWRITE 3222 static char * 3223 suffix_massage_regexize( const char *s ) 3224 { 3225 char *res, *ptr; 3226 const char *p, *r; 3227 int i; 3228 3229 if ( s[ 0 ] == '\0' ) { 3230 return ch_strdup( "^(.+)$" ); 3231 } 3232 3233 for ( i = 0, p = s; 3234 ( r = strchr( p, ',' ) ) != NULL; 3235 p = r + 1, i++ ) 3236 ; 3237 3238 res = ch_calloc( sizeof( char ), 3239 strlen( s ) 3240 + STRLENOF( "((.+),)?" ) 3241 + STRLENOF( "[ ]?" ) * i 3242 + STRLENOF( "$" ) + 1 ); 3243 3244 ptr = lutil_strcopy( res, "((.+),)?" ); 3245 for ( i = 0, p = s; 3246 ( r = strchr( p, ',' ) ) != NULL; 3247 p = r + 1 , i++ ) { 3248 ptr = lutil_strncopy( ptr, p, r - p + 1 ); 3249 ptr = lutil_strcopy( ptr, "[ ]?" ); 3250 3251 if ( r[ 1 ] == ' ' ) { 3252 r++; 3253 } 3254 } 3255 ptr = lutil_strcopy( ptr, p ); 3256 ptr[ 0 ] = '$'; 3257 ptr++; 3258 ptr[ 0 ] = '\0'; 3259 3260 return res; 3261 } 3262 3263 static char * 3264 suffix_massage_patternize( const char *s, const char *p ) 3265 { 3266 ber_len_t len; 3267 char *res, *ptr; 3268 3269 len = strlen( p ); 3270 3271 if ( s[ 0 ] == '\0' ) { 3272 len++; 3273 } 3274 3275 res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 ); 3276 if ( res == NULL ) { 3277 return NULL; 3278 } 3279 3280 ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) ); 3281 if ( s[ 0 ] == '\0' ) { 3282 ptr[ 0 ] = ','; 3283 ptr++; 3284 } 3285 lutil_strcopy( ptr, p ); 3286 3287 return res; 3288 } 3289 3290 int 3291 suffix_massage_config( 3292 struct rewrite_info *info, 3293 struct berval *pvnc, 3294 struct berval *nvnc, 3295 struct berval *prnc, 3296 struct berval *nrnc 3297 ) 3298 { 3299 char *rargv[ 5 ]; 3300 int line = 0; 3301 3302 rargv[ 0 ] = "rewriteEngine"; 3303 rargv[ 1 ] = "on"; 3304 rargv[ 2 ] = NULL; 3305 rewrite_parse( info, "<suffix massage>", ++line, 2, rargv ); 3306 3307 rargv[ 0 ] = "rewriteContext"; 3308 rargv[ 1 ] = "default"; 3309 rargv[ 2 ] = NULL; 3310 rewrite_parse( info, "<suffix massage>", ++line, 2, rargv ); 3311 3312 rargv[ 0 ] = "rewriteRule"; 3313 rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val ); 3314 rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val ); 3315 rargv[ 3 ] = ":"; 3316 rargv[ 4 ] = NULL; 3317 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3318 ch_free( rargv[ 1 ] ); 3319 ch_free( rargv[ 2 ] ); 3320 3321 if ( BER_BVISEMPTY( pvnc ) ) { 3322 rargv[ 0 ] = "rewriteRule"; 3323 rargv[ 1 ] = "^$"; 3324 rargv[ 2 ] = prnc->bv_val; 3325 rargv[ 3 ] = ":"; 3326 rargv[ 4 ] = NULL; 3327 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3328 } 3329 3330 rargv[ 0 ] = "rewriteContext"; 3331 rargv[ 1 ] = "searchEntryDN"; 3332 rargv[ 2 ] = NULL; 3333 rewrite_parse( info, "<suffix massage>", ++line, 2, rargv ); 3334 3335 rargv[ 0 ] = "rewriteRule"; 3336 rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val ); 3337 rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val ); 3338 rargv[ 3 ] = ":"; 3339 rargv[ 4 ] = NULL; 3340 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3341 ch_free( rargv[ 1 ] ); 3342 ch_free( rargv[ 2 ] ); 3343 3344 if ( BER_BVISEMPTY( prnc ) ) { 3345 rargv[ 0 ] = "rewriteRule"; 3346 rargv[ 1 ] = "^$"; 3347 rargv[ 2 ] = pvnc->bv_val; 3348 rargv[ 3 ] = ":"; 3349 rargv[ 4 ] = NULL; 3350 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3351 } 3352 3353 /* backward compatibility */ 3354 rargv[ 0 ] = "rewriteContext"; 3355 rargv[ 1 ] = "searchResult"; 3356 rargv[ 2 ] = "alias"; 3357 rargv[ 3 ] = "searchEntryDN"; 3358 rargv[ 4 ] = NULL; 3359 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3360 3361 rargv[ 0 ] = "rewriteContext"; 3362 rargv[ 1 ] = "matchedDN"; 3363 rargv[ 2 ] = "alias"; 3364 rargv[ 3 ] = "searchEntryDN"; 3365 rargv[ 4 ] = NULL; 3366 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3367 3368 rargv[ 0 ] = "rewriteContext"; 3369 rargv[ 1 ] = "searchAttrDN"; 3370 rargv[ 2 ] = "alias"; 3371 rargv[ 3 ] = "searchEntryDN"; 3372 rargv[ 4 ] = NULL; 3373 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv ); 3374 3375 /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE; 3376 * see servers/slapd/overlays/rwm.h for details */ 3377 rargv[ 0 ] = "rewriteContext"; 3378 rargv[ 1 ] = "referralAttrDN"; 3379 rargv[ 2 ] = NULL; 3380 rewrite_parse( info, "<suffix massage>", ++line, 2, rargv ); 3381 3382 rargv[ 0 ] = "rewriteContext"; 3383 rargv[ 1 ] = "referralDN"; 3384 rargv[ 2 ] = NULL; 3385 rewrite_parse( info, "<suffix massage>", ++line, 2, rargv ); 3386 3387 return 0; 3388 } 3389 #endif /* ENABLE_REWRITE */ 3390 3391