1 /* $NetBSD: lastmod.c,v 1.1.1.4 2014/05/28 09:58:27 tron Exp $ */ 2 3 /* lastmod.c - returns last modification info */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2004-2014 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 /* ACKNOWLEDGEMENTS: 19 * This work was initially developed by Pierangelo Masarati for inclusion in 20 * OpenLDAP Software. 21 */ 22 23 #include "portable.h" 24 25 #ifdef SLAPD_OVER_LASTMOD 26 27 #include <stdio.h> 28 29 #include <ac/string.h> 30 #include <ac/socket.h> 31 32 #include "slap.h" 33 #include "lutil.h" 34 35 typedef struct lastmod_info_t { 36 struct berval lmi_rdnvalue; 37 Entry *lmi_e; 38 ldap_pvt_thread_mutex_t lmi_entry_mutex; 39 int lmi_enabled; 40 } lastmod_info_t; 41 42 struct lastmod_schema_t { 43 ObjectClass *lms_oc_lastmod; 44 AttributeDescription *lms_ad_lastmodDN; 45 AttributeDescription *lms_ad_lastmodType; 46 AttributeDescription *lms_ad_lastmodEnabled; 47 } lastmod_schema; 48 49 enum lastmodType_e { 50 LASTMOD_ADD = 0, 51 LASTMOD_DELETE, 52 LASTMOD_EXOP, 53 LASTMOD_MODIFY, 54 LASTMOD_MODRDN, 55 LASTMOD_UNKNOWN 56 }; 57 58 struct berval lastmodType[] = { 59 BER_BVC( "add" ), 60 BER_BVC( "delete" ), 61 BER_BVC( "exop" ), 62 BER_BVC( "modify" ), 63 BER_BVC( "modrdn" ), 64 BER_BVC( "unknown" ), 65 BER_BVNULL 66 }; 67 68 static struct m_s { 69 char *schema; 70 slap_mask_t flags; 71 int offset; 72 } moc[] = { 73 { "( 1.3.6.1.4.1.4203.666.3.13" 74 "NAME 'lastmod' " 75 "DESC 'OpenLDAP per-database last modification monitoring' " 76 "STRUCTURAL " 77 "SUP top " 78 "MUST cn " 79 "MAY ( " 80 "lastmodDN " 81 "$ lastmodType " 82 "$ description " 83 "$ seeAlso " 84 ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE, 85 offsetof( struct lastmod_schema_t, lms_oc_lastmod ) }, 86 { NULL } 87 }, mat[] = { 88 { "( 1.3.6.1.4.1.4203.666.1.28" 89 "NAME 'lastmodDN' " 90 "DESC 'DN of last modification' " 91 "EQUALITY distinguishedNameMatch " 92 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " 93 "NO-USER-MODIFICATION " 94 "USAGE directoryOperation )", SLAP_AT_HIDE, 95 offsetof( struct lastmod_schema_t, lms_ad_lastmodDN ) }, 96 { "( 1.3.6.1.4.1.4203.666.1.29" 97 "NAME 'lastmodType' " 98 "DESC 'Type of last modification' " 99 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " 100 "EQUALITY caseIgnoreMatch " 101 "SINGLE-VALUE " 102 "NO-USER-MODIFICATION " 103 "USAGE directoryOperation )", SLAP_AT_HIDE, 104 offsetof( struct lastmod_schema_t, lms_ad_lastmodType ) }, 105 { "( 1.3.6.1.4.1.4203.666.1.30" 106 "NAME 'lastmodEnabled' " 107 "DESC 'Lastmod overlay state' " 108 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 " 109 "EQUALITY booleanMatch " 110 "SINGLE-VALUE )", 0, 111 offsetof( struct lastmod_schema_t, lms_ad_lastmodEnabled ) }, 112 { NULL } 113 114 /* FIXME: what about UUID of last modified entry? */ 115 }; 116 117 static int 118 lastmod_search( Operation *op, SlapReply *rs ) 119 { 120 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 121 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 122 int rc; 123 124 /* if we get here, it must be a success */ 125 rs->sr_err = LDAP_SUCCESS; 126 127 ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex ); 128 129 rc = test_filter( op, lmi->lmi_e, op->oq_search.rs_filter ); 130 if ( rc == LDAP_COMPARE_TRUE ) { 131 rs->sr_attrs = op->ors_attrs; 132 rs->sr_flags = 0; 133 rs->sr_entry = lmi->lmi_e; 134 rs->sr_err = send_search_entry( op, rs ); 135 rs->sr_entry = NULL; 136 rs->sr_flags = 0; 137 rs->sr_attrs = NULL; 138 } 139 140 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 141 142 send_ldap_result( op, rs ); 143 144 return 0; 145 } 146 147 static int 148 lastmod_compare( Operation *op, SlapReply *rs ) 149 { 150 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 151 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 152 Attribute *a; 153 154 ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex ); 155 156 if ( get_assert( op ) && 157 ( test_filter( op, lmi->lmi_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) ) 158 { 159 rs->sr_err = LDAP_ASSERTION_FAILED; 160 goto return_results; 161 } 162 163 rs->sr_err = access_allowed( op, lmi->lmi_e, op->oq_compare.rs_ava->aa_desc, 164 &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ); 165 if ( ! rs->sr_err ) { 166 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 167 goto return_results; 168 } 169 170 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE; 171 172 for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc ); 173 a != NULL; 174 a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) ) 175 { 176 rs->sr_err = LDAP_COMPARE_FALSE; 177 178 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc, 179 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 180 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 181 a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 ) 182 { 183 rs->sr_err = LDAP_COMPARE_TRUE; 184 break; 185 } 186 } 187 188 return_results:; 189 190 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 191 192 send_ldap_result( op, rs ); 193 194 if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) { 195 rs->sr_err = LDAP_SUCCESS; 196 } 197 198 return rs->sr_err; 199 } 200 201 static int 202 lastmod_exop( Operation *op, SlapReply *rs ) 203 { 204 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 205 206 /* Temporary */ 207 208 op->o_bd->bd_info = (BackendInfo *)on->on_info; 209 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 210 rs->sr_text = "not allowed within namingContext"; 211 send_ldap_result( op, rs ); 212 rs->sr_text = NULL; 213 214 return -1; 215 } 216 217 static int 218 lastmod_modify( Operation *op, SlapReply *rs ) 219 { 220 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 221 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 222 Modifications *ml; 223 224 ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex ); 225 226 if ( !acl_check_modlist( op, lmi->lmi_e, op->orm_modlist ) ) { 227 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 228 goto cleanup; 229 } 230 231 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) { 232 Attribute *a; 233 234 if ( ml->sml_desc != lastmod_schema.lms_ad_lastmodEnabled ) { 235 continue; 236 } 237 238 if ( ml->sml_op != LDAP_MOD_REPLACE ) { 239 rs->sr_text = "unsupported mod type"; 240 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 241 goto cleanup; 242 } 243 244 a = attr_find( lmi->lmi_e->e_attrs, ml->sml_desc ); 245 246 if ( a == NULL ) { 247 rs->sr_text = "lastmod overlay internal error"; 248 rs->sr_err = LDAP_OTHER; 249 goto cleanup; 250 } 251 252 ch_free( a->a_vals[ 0 ].bv_val ); 253 ber_dupbv( &a->a_vals[ 0 ], &ml->sml_values[ 0 ] ); 254 if ( a->a_nvals ) { 255 ch_free( a->a_nvals[ 0 ].bv_val ); 256 if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) { 257 ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_nvalues[ 0 ] ); 258 } else { 259 ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_values[ 0 ] ); 260 } 261 } 262 263 if ( strcmp( ml->sml_values[ 0 ].bv_val, "TRUE" ) == 0 ) { 264 lmi->lmi_enabled = 1; 265 } else if ( strcmp( ml->sml_values[ 0 ].bv_val, "FALSE" ) == 0 ) { 266 lmi->lmi_enabled = 0; 267 } else { 268 assert( 0 ); 269 } 270 } 271 272 rs->sr_err = LDAP_SUCCESS; 273 274 cleanup:; 275 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 276 277 send_ldap_result( op, rs ); 278 rs->sr_text = NULL; 279 280 return rs->sr_err; 281 } 282 283 static int 284 lastmod_op_func( Operation *op, SlapReply *rs ) 285 { 286 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 287 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 288 Modifications *ml; 289 290 if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) { 291 switch ( op->o_tag ) { 292 case LDAP_REQ_SEARCH: 293 if ( op->ors_scope != LDAP_SCOPE_BASE ) { 294 goto return_referral; 295 } 296 /* process */ 297 return lastmod_search( op, rs ); 298 299 case LDAP_REQ_COMPARE: 300 return lastmod_compare( op, rs ); 301 302 case LDAP_REQ_EXTENDED: 303 /* if write, reject; otherwise process */ 304 if ( exop_is_write( op )) { 305 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 306 rs->sr_text = "not allowed within namingContext"; 307 goto return_error; 308 } 309 return lastmod_exop( op, rs ); 310 311 case LDAP_REQ_MODIFY: 312 /* allow only changes to overlay status */ 313 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) { 314 if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifiersName ) != 0 315 && ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) != 0 316 && ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) != 0 317 && ad_cmp( ml->sml_desc, lastmod_schema.lms_ad_lastmodEnabled ) != 0 ) 318 { 319 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 320 rs->sr_text = "not allowed within namingContext"; 321 goto return_error; 322 } 323 } 324 return lastmod_modify( op, rs ); 325 326 default: 327 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 328 rs->sr_text = "not allowed within namingContext"; 329 goto return_error; 330 } 331 } 332 333 if ( dnIsSuffix( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) { 334 goto return_referral; 335 } 336 337 return SLAP_CB_CONTINUE; 338 339 return_referral:; 340 op->o_bd->bd_info = (BackendInfo *)on->on_info; 341 rs->sr_ref = referral_rewrite( default_referral, 342 NULL, &op->o_req_dn, op->ors_scope ); 343 344 if ( !rs->sr_ref ) { 345 rs->sr_ref = default_referral; 346 } 347 rs->sr_err = LDAP_REFERRAL; 348 send_ldap_result( op, rs ); 349 350 if ( rs->sr_ref != default_referral ) { 351 ber_bvarray_free( rs->sr_ref ); 352 } 353 rs->sr_ref = NULL; 354 355 return -1; 356 357 return_error:; 358 op->o_bd->bd_info = (BackendInfo *)on->on_info; 359 send_ldap_result( op, rs ); 360 rs->sr_text = NULL; 361 362 return -1; 363 } 364 365 static int 366 best_guess( Operation *op, 367 struct berval *bv_entryCSN, struct berval *bv_nentryCSN, 368 struct berval *bv_modifyTimestamp, struct berval *bv_nmodifyTimestamp, 369 struct berval *bv_modifiersName, struct berval *bv_nmodifiersName ) 370 { 371 if ( bv_entryCSN ) { 372 char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ]; 373 struct berval entryCSN; 374 375 entryCSN.bv_val = csnbuf; 376 entryCSN.bv_len = sizeof( csnbuf ); 377 slap_get_csn( NULL, &entryCSN, 0 ); 378 379 ber_dupbv( bv_entryCSN, &entryCSN ); 380 ber_dupbv( bv_nentryCSN, &entryCSN ); 381 } 382 383 if ( bv_modifyTimestamp ) { 384 char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 385 struct berval timestamp; 386 time_t currtime; 387 388 /* best guess */ 389 #if 0 390 currtime = slap_get_time(); 391 #endif 392 /* maybe we better use the time the operation was initiated */ 393 currtime = op->o_time; 394 395 timestamp.bv_val = tmbuf; 396 timestamp.bv_len = sizeof(tmbuf); 397 slap_timestamp( &currtime, ×tamp ); 398 399 ber_dupbv( bv_modifyTimestamp, ×tamp ); 400 ber_dupbv( bv_nmodifyTimestamp, bv_modifyTimestamp ); 401 } 402 403 if ( bv_modifiersName ) { 404 /* best guess */ 405 ber_dupbv( bv_modifiersName, &op->o_dn ); 406 ber_dupbv( bv_nmodifiersName, &op->o_ndn ); 407 } 408 409 return 0; 410 } 411 412 static int 413 lastmod_update( Operation *op, SlapReply *rs ) 414 { 415 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 416 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 417 Attribute *a; 418 Modifications *ml = NULL; 419 struct berval bv_entryCSN = BER_BVNULL, 420 bv_nentryCSN = BER_BVNULL, 421 bv_modifyTimestamp = BER_BVNULL, 422 bv_nmodifyTimestamp = BER_BVNULL, 423 bv_modifiersName = BER_BVNULL, 424 bv_nmodifiersName = BER_BVNULL, 425 bv_name = BER_BVNULL, 426 bv_nname = BER_BVNULL; 427 enum lastmodType_e lmt = LASTMOD_UNKNOWN; 428 Entry *e = NULL; 429 int rc = -1; 430 431 /* FIXME: timestamp? modifier? */ 432 switch ( op->o_tag ) { 433 case LDAP_REQ_ADD: 434 lmt = LASTMOD_ADD; 435 e = op->ora_e; 436 a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN ); 437 if ( a != NULL ) { 438 ber_dupbv( &bv_entryCSN, &a->a_vals[0] ); 439 if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) { 440 ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] ); 441 } else { 442 ber_dupbv( &bv_nentryCSN, &a->a_vals[0] ); 443 } 444 } 445 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp ); 446 if ( a != NULL ) { 447 ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] ); 448 if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) { 449 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] ); 450 } else { 451 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] ); 452 } 453 } 454 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName ); 455 if ( a != NULL ) { 456 ber_dupbv( &bv_modifiersName, &a->a_vals[0] ); 457 ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] ); 458 } 459 ber_dupbv( &bv_name, &e->e_name ); 460 ber_dupbv( &bv_nname, &e->e_nname ); 461 break; 462 463 case LDAP_REQ_DELETE: 464 lmt = LASTMOD_DELETE; 465 466 best_guess( op, &bv_entryCSN, &bv_nentryCSN, 467 &bv_modifyTimestamp, &bv_nmodifyTimestamp, 468 &bv_modifiersName, &bv_nmodifiersName ); 469 470 ber_dupbv( &bv_name, &op->o_req_dn ); 471 ber_dupbv( &bv_nname, &op->o_req_ndn ); 472 break; 473 474 case LDAP_REQ_EXTENDED: 475 lmt = LASTMOD_EXOP; 476 477 /* actually, password change is wrapped around a backend 478 * call to modify, so it never shows up as an exop... */ 479 best_guess( op, &bv_entryCSN, &bv_nentryCSN, 480 &bv_modifyTimestamp, &bv_nmodifyTimestamp, 481 &bv_modifiersName, &bv_nmodifiersName ); 482 483 ber_dupbv( &bv_name, &op->o_req_dn ); 484 ber_dupbv( &bv_nname, &op->o_req_ndn ); 485 break; 486 487 case LDAP_REQ_MODIFY: 488 lmt = LASTMOD_MODIFY; 489 rc = 3; 490 491 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) { 492 if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) { 493 ber_dupbv( &bv_modifiersName, &ml->sml_values[0] ); 494 ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] ); 495 496 rc--; 497 if ( !rc ) { 498 break; 499 } 500 501 } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) == 0 ) { 502 ber_dupbv( &bv_entryCSN, &ml->sml_values[0] ); 503 if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) { 504 ber_dupbv( &bv_nentryCSN, &ml->sml_nvalues[0] ); 505 } else { 506 ber_dupbv( &bv_nentryCSN, &ml->sml_values[0] ); 507 } 508 509 rc --; 510 if ( !rc ) { 511 break; 512 } 513 514 } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) == 0 ) { 515 ber_dupbv( &bv_modifyTimestamp, &ml->sml_values[0] ); 516 if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) { 517 ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_nvalues[0] ); 518 } else { 519 ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] ); 520 } 521 522 rc --; 523 if ( !rc ) { 524 break; 525 } 526 } 527 } 528 529 /* if rooted at global overlay, opattrs are not yet in place */ 530 if ( BER_BVISNULL( &bv_modifiersName ) ) { 531 best_guess( op, NULL, NULL, NULL, NULL, &bv_modifiersName, &bv_nmodifiersName ); 532 } 533 534 if ( BER_BVISNULL( &bv_entryCSN ) ) { 535 best_guess( op, &bv_entryCSN, &bv_nentryCSN, NULL, NULL, NULL, NULL ); 536 } 537 538 if ( BER_BVISNULL( &bv_modifyTimestamp ) ) { 539 best_guess( op, NULL, NULL, &bv_modifyTimestamp, &bv_nmodifyTimestamp, NULL, NULL ); 540 } 541 542 ber_dupbv( &bv_name, &op->o_req_dn ); 543 ber_dupbv( &bv_nname, &op->o_req_ndn ); 544 break; 545 546 case LDAP_REQ_MODRDN: 547 lmt = LASTMOD_MODRDN; 548 e = NULL; 549 550 if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) { 551 build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL ); 552 build_new_dn( &bv_nname, op->orr_nnewSup, &op->orr_nnewrdn, NULL ); 553 554 } else { 555 struct berval pdn; 556 557 dnParent( &op->o_req_dn, &pdn ); 558 build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL ); 559 560 dnParent( &op->o_req_ndn, &pdn ); 561 build_new_dn( &bv_nname, &pdn, &op->orr_nnewrdn, NULL ); 562 } 563 564 if ( on->on_info->oi_orig->bi_entry_get_rw ) { 565 BackendInfo *bi = op->o_bd->bd_info; 566 int rc; 567 568 op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig; 569 rc = op->o_bd->bd_info->bi_entry_get_rw( op, &bv_name, NULL, NULL, 0, &e ); 570 if ( rc == LDAP_SUCCESS ) { 571 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName ); 572 if ( a != NULL ) { 573 ber_dupbv( &bv_modifiersName, &a->a_vals[0] ); 574 ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] ); 575 } 576 a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN ); 577 if ( a != NULL ) { 578 ber_dupbv( &bv_entryCSN, &a->a_vals[0] ); 579 if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) { 580 ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] ); 581 } else { 582 ber_dupbv( &bv_nentryCSN, &a->a_vals[0] ); 583 } 584 } 585 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp ); 586 if ( a != NULL ) { 587 ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] ); 588 if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) { 589 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] ); 590 } else { 591 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] ); 592 } 593 } 594 595 assert( dn_match( &bv_name, &e->e_name ) ); 596 assert( dn_match( &bv_nname, &e->e_nname ) ); 597 598 op->o_bd->bd_info->bi_entry_release_rw( op, e, 0 ); 599 } 600 601 op->o_bd->bd_info = bi; 602 603 } 604 605 /* if !bi_entry_get_rw || bi_entry_get_rw failed for any reason... */ 606 if ( e == NULL ) { 607 best_guess( op, &bv_entryCSN, &bv_nentryCSN, 608 &bv_modifyTimestamp, &bv_nmodifyTimestamp, 609 &bv_modifiersName, &bv_nmodifiersName ); 610 } 611 612 break; 613 614 default: 615 return -1; 616 } 617 618 ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex ); 619 620 #if 0 621 fprintf( stderr, "### lastmodDN: %s %s\n", bv_name.bv_val, bv_nname.bv_val ); 622 #endif 623 624 a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodDN ); 625 if ( a == NULL ) { 626 goto error_return; 627 } 628 ch_free( a->a_vals[0].bv_val ); 629 a->a_vals[0] = bv_name; 630 ch_free( a->a_nvals[0].bv_val ); 631 a->a_nvals[0] = bv_nname; 632 633 #if 0 634 fprintf( stderr, "### lastmodType: %s %s\n", lastmodType[ lmt ].bv_val, lastmodType[ lmt ].bv_val ); 635 #endif 636 637 a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodType ); 638 if ( a == NULL ) { 639 goto error_return; 640 } 641 ch_free( a->a_vals[0].bv_val ); 642 ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] ); 643 ch_free( a->a_nvals[0].bv_val ); 644 ber_dupbv( &a->a_nvals[0], &lastmodType[ lmt ] ); 645 646 #if 0 647 fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val ); 648 #endif 649 650 a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifiersName ); 651 if ( a == NULL ) { 652 goto error_return; 653 } 654 ch_free( a->a_vals[0].bv_val ); 655 a->a_vals[0] = bv_modifiersName; 656 ch_free( a->a_nvals[0].bv_val ); 657 a->a_nvals[0] = bv_nmodifiersName; 658 659 #if 0 660 fprintf( stderr, "### modifyTimestamp: %s %s\n", bv_nmodifyTimestamp.bv_val, bv_modifyTimestamp.bv_val ); 661 #endif 662 663 a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifyTimestamp ); 664 if ( a == NULL ) { 665 goto error_return; 666 } 667 ch_free( a->a_vals[0].bv_val ); 668 a->a_vals[0] = bv_modifyTimestamp; 669 ch_free( a->a_nvals[0].bv_val ); 670 a->a_nvals[0] = bv_nmodifyTimestamp; 671 672 #if 0 673 fprintf( stderr, "### entryCSN: %s %s\n", bv_nentryCSN.bv_val, bv_entryCSN.bv_val ); 674 #endif 675 676 a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_entryCSN ); 677 if ( a == NULL ) { 678 goto error_return; 679 } 680 ch_free( a->a_vals[0].bv_val ); 681 a->a_vals[0] = bv_entryCSN; 682 ch_free( a->a_nvals[0].bv_val ); 683 a->a_nvals[0] = bv_nentryCSN; 684 685 rc = 0; 686 687 error_return:; 688 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 689 690 return rc; 691 } 692 693 static int 694 lastmod_response( Operation *op, SlapReply *rs ) 695 { 696 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 697 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 698 699 /* don't record failed operations */ 700 switch ( rs->sr_err ) { 701 case LDAP_SUCCESS: 702 /* FIXME: other cases? */ 703 break; 704 705 default: 706 return SLAP_CB_CONTINUE; 707 } 708 709 /* record only write operations */ 710 switch ( op->o_tag ) { 711 case LDAP_REQ_ADD: 712 case LDAP_REQ_MODIFY: 713 case LDAP_REQ_MODRDN: 714 case LDAP_REQ_DELETE: 715 break; 716 717 case LDAP_REQ_EXTENDED: 718 /* if write, process */ 719 if ( exop_is_write( op )) 720 break; 721 722 /* fall thru */ 723 default: 724 return SLAP_CB_CONTINUE; 725 } 726 727 /* skip if disabled */ 728 ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex ); 729 if ( !lmi->lmi_enabled ) { 730 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 731 return SLAP_CB_CONTINUE; 732 } 733 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex ); 734 735 (void)lastmod_update( op, rs ); 736 737 return SLAP_CB_CONTINUE; 738 } 739 740 static int 741 lastmod_db_init( 742 BackendDB *be 743 ) 744 { 745 slap_overinst *on = (slap_overinst *)be->bd_info; 746 lastmod_info_t *lmi; 747 748 if ( lastmod_schema.lms_oc_lastmod == NULL ) { 749 int i; 750 const char *text; 751 752 /* schema integration */ 753 for ( i = 0; mat[i].schema; i++ ) { 754 int code; 755 AttributeDescription **ad = 756 ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset])); 757 ad[0] = NULL; 758 759 code = register_at( mat[i].schema, ad, 0 ); 760 if ( code ) { 761 Debug( LDAP_DEBUG_ANY, 762 "lastmod_init: register_at failed\n", 0, 0, 0 ); 763 return -1; 764 } 765 (*ad)->ad_type->sat_flags |= mat[i].flags; 766 } 767 768 for ( i = 0; moc[i].schema; i++ ) { 769 int code; 770 ObjectClass **Oc = 771 ((ObjectClass **)&(((char *)&lastmod_schema)[moc[i].offset])); 772 773 code = register_oc( moc[i].schema, Oc, 0 ); 774 if ( code ) { 775 Debug( LDAP_DEBUG_ANY, 776 "lastmod_init: register_oc failed\n", 0, 0, 0 ); 777 return -1; 778 } 779 (*Oc)->soc_flags |= moc[i].flags; 780 } 781 } 782 783 lmi = (lastmod_info_t *)ch_malloc( sizeof( lastmod_info_t ) ); 784 785 memset( lmi, 0, sizeof( lastmod_info_t ) ); 786 lmi->lmi_enabled = 1; 787 788 on->on_bi.bi_private = lmi; 789 790 return 0; 791 } 792 793 static int 794 lastmod_db_config( 795 BackendDB *be, 796 const char *fname, 797 int lineno, 798 int argc, 799 char **argv 800 ) 801 { 802 slap_overinst *on = (slap_overinst *)be->bd_info; 803 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 804 805 if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) { 806 if ( lmi->lmi_rdnvalue.bv_val ) { 807 /* already defined! */ 808 ch_free( lmi->lmi_rdnvalue.bv_val ); 809 } 810 811 ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue ); 812 813 } else if ( strcasecmp( argv[ 0 ], "lastmod-enabled" ) == 0 ) { 814 if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) { 815 lmi->lmi_enabled = 1; 816 817 } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) { 818 lmi->lmi_enabled = 0; 819 820 } else { 821 return -1; 822 } 823 824 } else { 825 return SLAP_CONF_UNKNOWN; 826 } 827 828 return 0; 829 } 830 831 static int 832 lastmod_db_open( 833 BackendDB *be 834 ) 835 { 836 slap_overinst *on = (slap_overinst *) be->bd_info; 837 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 838 char buf[ 8192 ]; 839 static char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 840 841 char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ]; 842 struct berval entryCSN; 843 struct berval timestamp; 844 845 if ( !SLAP_LASTMOD( be ) ) { 846 fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" ); 847 return -1; 848 } 849 850 /* 851 * Start 852 */ 853 timestamp.bv_val = tmbuf; 854 timestamp.bv_len = sizeof(tmbuf); 855 slap_timestamp( &starttime, ×tamp ); 856 857 entryCSN.bv_val = csnbuf; 858 entryCSN.bv_len = sizeof( csnbuf ); 859 slap_get_csn( NULL, &entryCSN, 0 ); 860 861 if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) { 862 ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue ); 863 } 864 865 snprintf( buf, sizeof( buf ), 866 "dn: cn=%s%s%s\n" 867 "objectClass: %s\n" 868 "structuralObjectClass: %s\n" 869 "cn: %s\n" 870 "description: This object contains the last modification to this database\n" 871 "%s: cn=%s%s%s\n" 872 "%s: %s\n" 873 "%s: %s\n" 874 "createTimestamp: %s\n" 875 "creatorsName: %s\n" 876 "entryCSN: %s\n" 877 "modifyTimestamp: %s\n" 878 "modifiersName: %s\n" 879 "hasSubordinates: FALSE\n", 880 lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val, 881 lastmod_schema.lms_oc_lastmod->soc_cname.bv_val, 882 lastmod_schema.lms_oc_lastmod->soc_cname.bv_val, 883 lmi->lmi_rdnvalue.bv_val, 884 lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val, 885 lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val, 886 lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val, 887 lastmod_schema.lms_ad_lastmodEnabled->ad_cname.bv_val, lmi->lmi_enabled ? "TRUE" : "FALSE", 888 tmbuf, 889 BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val, 890 entryCSN.bv_val, 891 tmbuf, 892 BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val ); 893 894 #if 0 895 fprintf( stderr, "# entry:\n%s\n", buf ); 896 #endif 897 898 lmi->lmi_e = str2entry( buf ); 899 if ( lmi->lmi_e == NULL ) { 900 return -1; 901 } 902 903 ldap_pvt_thread_mutex_init( &lmi->lmi_entry_mutex ); 904 905 return 0; 906 } 907 908 static int 909 lastmod_db_destroy( 910 BackendDB *be 911 ) 912 { 913 slap_overinst *on = (slap_overinst *)be->bd_info; 914 lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private; 915 916 if ( lmi ) { 917 if ( !BER_BVISNULL( &lmi->lmi_rdnvalue ) ) { 918 ch_free( lmi->lmi_rdnvalue.bv_val ); 919 } 920 921 if ( lmi->lmi_e ) { 922 entry_free( lmi->lmi_e ); 923 924 ldap_pvt_thread_mutex_destroy( &lmi->lmi_entry_mutex ); 925 } 926 927 ch_free( lmi ); 928 } 929 930 return 0; 931 } 932 933 /* This overlay is set up for dynamic loading via moduleload. For static 934 * configuration, you'll need to arrange for the slap_overinst to be 935 * initialized and registered by some other function inside slapd. 936 */ 937 938 static slap_overinst lastmod; 939 940 int 941 lastmod_initialize() 942 { 943 lastmod.on_bi.bi_type = "lastmod"; 944 lastmod.on_bi.bi_db_init = lastmod_db_init; 945 lastmod.on_bi.bi_db_config = lastmod_db_config; 946 lastmod.on_bi.bi_db_destroy = lastmod_db_destroy; 947 lastmod.on_bi.bi_db_open = lastmod_db_open; 948 949 lastmod.on_bi.bi_op_add = lastmod_op_func; 950 lastmod.on_bi.bi_op_compare = lastmod_op_func; 951 lastmod.on_bi.bi_op_delete = lastmod_op_func; 952 lastmod.on_bi.bi_op_modify = lastmod_op_func; 953 lastmod.on_bi.bi_op_modrdn = lastmod_op_func; 954 lastmod.on_bi.bi_op_search = lastmod_op_func; 955 lastmod.on_bi.bi_extended = lastmod_op_func; 956 957 lastmod.on_response = lastmod_response; 958 959 return overlay_register( &lastmod ); 960 } 961 962 #if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC 963 int 964 init_module( int argc, char *argv[] ) 965 { 966 return lastmod_initialize(); 967 } 968 #endif /* SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC */ 969 970 #endif /* defined(SLAPD_OVER_LASTMOD) */ 971