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