1/* vernum.c - RDN value overlay */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-2011 The OpenLDAP Foundation. 6 * Portions Copyright 2008 Pierangelo Masarati. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* ACKNOWLEDGEMENTS: 18 * This work was initially developed by Pierangelo Masarati 19 * for inclusion in OpenLDAP Software. 20 */ 21 22#include "portable.h" 23 24#ifdef SLAPD_OVER_VERNUM 25 26#include <stdio.h> 27 28#include "ac/string.h" 29#include "ac/socket.h" 30 31#include "slap.h" 32#include "config.h" 33 34#include "lutil.h" 35 36/* 37 * Maintain an attribute (e.g. msDS-KeyVersionNumber) that consists 38 * in a counter of modifications of another attribute (e.g. unicodePwd). 39 */ 40 41typedef struct vernum_t { 42 AttributeDescription *vn_attr; 43 AttributeDescription *vn_vernum; 44} vernum_t; 45 46static AttributeDescription *ad_msDS_KeyVersionNumber; 47 48static struct berval val_init = BER_BVC( "0" ); 49static slap_overinst vernum; 50 51static int 52vernum_op_add( Operation *op, SlapReply *rs ) 53{ 54 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 55 vernum_t *vn = (vernum_t *)on->on_bi.bi_private; 56 57 Attribute *a, **ap; 58 int rc; 59 60 /* NOTE: should we accept an entry still in mods format? */ 61 assert( op->ora_e != NULL ); 62 63 if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) { 64 return SLAP_CB_CONTINUE; 65 } 66 67 a = attr_find( op->ora_e->e_attrs, vn->vn_attr ); 68 if ( a == NULL ) { 69 return SLAP_CB_CONTINUE; 70 } 71 72 if ( attr_find( op->ora_e->e_attrs, vn->vn_vernum ) != NULL ) { 73 /* already present - leave it alone */ 74 return SLAP_CB_CONTINUE; 75 } 76 77 a = attr_alloc( vn->vn_vernum ); 78 79 value_add_one( &a->a_vals, &val_init ); 80 a->a_nvals = a->a_vals; 81 a->a_numvals = 1; 82 83 for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next ) 84 /* goto tail */ ; 85 86 *ap = a; 87 88 return SLAP_CB_CONTINUE; 89} 90 91static int 92vernum_op_modify( Operation *op, SlapReply *rs ) 93{ 94 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 95 vernum_t *vn = (vernum_t *)on->on_bi.bi_private; 96 97 Modifications *ml, **mlp; 98 struct berval val = BER_BVC( "1" ); 99 int rc; 100 unsigned got = 0; 101 102 for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next ) { 103 if ( ml->sml_desc == vn->vn_vernum ) { 104 /* already present - leave it alone 105 * (or should we increment it anyway?) */ 106 return SLAP_CB_CONTINUE; 107 } 108 109 if ( ml->sml_desc == vn->vn_attr ) { 110 got = 1; 111 } 112 } 113 114 if ( !got ) { 115 return SLAP_CB_CONTINUE; 116 } 117 118 for ( mlp = &op->orm_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next ) 119 /* goto tail */ ; 120 121 /* ITS#6561 */ 122#ifdef SLAP_MOD_ADD_IF_NOT_PRESENT 123 /* the initial value is only added if the vernum attr is not present */ 124 ml = SLAP_CALLOC( sizeof( Modifications ), 1 ); 125 ml->sml_values = SLAP_CALLOC( sizeof( struct berval ) , 2 ); 126 value_add_one( &ml->sml_values, &val_init ); 127 ml->sml_nvalues = NULL; 128 ml->sml_numvals = 1; 129 ml->sml_op = SLAP_MOD_ADD_IF_NOT_PRESENT; 130 ml->sml_flags = SLAP_MOD_INTERNAL; 131 ml->sml_desc = vn->vn_vernum; 132 ml->sml_type = vn->vn_vernum->ad_cname; 133 134 *mlp = ml; 135 mlp = &ml->sml_next; 136#endif /* SLAP_MOD_ADD_IF_NOT_PRESENT */ 137 138 /* this increments by 1 the vernum attr */ 139 ml = SLAP_CALLOC( sizeof( Modifications ), 1 ); 140 ml->sml_values = SLAP_CALLOC( sizeof( struct berval ) , 2 ); 141 value_add_one( &ml->sml_values, &val ); 142 ml->sml_nvalues = NULL; 143 ml->sml_numvals = 1; 144 ml->sml_op = LDAP_MOD_INCREMENT; 145 ml->sml_flags = SLAP_MOD_INTERNAL; 146 ml->sml_desc = vn->vn_vernum; 147 ml->sml_type = vn->vn_vernum->ad_cname; 148 149 *mlp = ml; 150 151 return SLAP_CB_CONTINUE; 152} 153 154static int 155vernum_db_init( 156 BackendDB *be, 157 ConfigReply *cr) 158{ 159 slap_overinst *on = (slap_overinst *) be->bd_info; 160 vernum_t *vn = NULL; 161 162 if ( SLAP_ISGLOBALOVERLAY( be ) ) { 163 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 164 "vernum_db_init: vernum cannot be used as global overlay.\n" ); 165 return 1; 166 } 167 168 if ( be->be_nsuffix == NULL ) { 169 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 170 "vernum_db_init: database must have suffix\n" ); 171 return 1; 172 } 173 174 if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) { 175 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 176 "vernum_db_init: missing rootdn for database DN=\"%s\", YMMV\n", 177 be->be_suffix[ 0 ].bv_val ); 178 } 179 180 vn = (vernum_t *)ch_calloc( 1, sizeof( vernum_t ) ); 181 182 on->on_bi.bi_private = (void *)vn; 183 184 return 0; 185} 186 187typedef struct vernum_mod_t { 188 struct berval ndn; 189 struct vernum_mod_t *next; 190} vernum_mod_t; 191 192typedef struct { 193 BackendDB *bd; 194 vernum_mod_t *mods; 195} vernum_repair_cb_t; 196 197static int 198vernum_repair_cb( Operation *op, SlapReply *rs ) 199{ 200 int rc; 201 vernum_repair_cb_t *rcb = op->o_callback->sc_private; 202 vernum_mod_t *mod; 203 ber_len_t len; 204 BackendDB *save_bd = op->o_bd; 205 206 switch ( rs->sr_type ) { 207 case REP_SEARCH: 208 break; 209 210 case REP_SEARCHREF: 211 case REP_RESULT: 212 return rs->sr_err; 213 214 default: 215 assert( 0 ); 216 } 217 218 assert( rs->sr_entry != NULL ); 219 220 len = sizeof( vernum_mod_t ) + rs->sr_entry->e_nname.bv_len + 1; 221 mod = op->o_tmpalloc( len, op->o_tmpmemctx ); 222 mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len; 223 mod->ndn.bv_val = (char *)&mod[1]; 224 lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len ); 225 226 mod->next = rcb->mods; 227 rcb->mods = mod; 228 229 Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair_cb: scheduling entry DN=\"%s\" for repair\n", 230 op->o_log_prefix, rs->sr_entry->e_name.bv_val, 0 ); 231 232 return 0; 233} 234 235static int 236vernum_repair( BackendDB *be ) 237{ 238 slap_overinst *on = (slap_overinst *)be->bd_info; 239 vernum_t *vn = (vernum_t *)on->on_bi.bi_private; 240 void *ctx = ldap_pvt_thread_pool_context(); 241 Connection conn = { 0 }; 242 OperationBuffer opbuf; 243 Operation *op; 244 BackendDB db; 245 slap_callback sc = { 0 }; 246 vernum_repair_cb_t rcb = { 0 }; 247 SlapReply rs = { REP_RESULT }; 248 vernum_mod_t *rmod; 249 int nrepaired = 0; 250 251 connection_fake_init2( &conn, &opbuf, ctx, 0 ); 252 op = &opbuf.ob_op; 253 254 op->o_tag = LDAP_REQ_SEARCH; 255 memset( &op->oq_search, 0, sizeof( op->oq_search ) ); 256 257 assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) ); 258 259 op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 ); 260 assert( op->o_bd != NULL ); 261 assert( op->o_bd->be_nsuffix != NULL ); 262 263 op->o_req_dn = op->o_bd->be_suffix[ 0 ]; 264 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ]; 265 266 op->o_dn = op->o_bd->be_rootdn; 267 op->o_ndn = op->o_bd->be_rootndn; 268 269 op->ors_scope = LDAP_SCOPE_SUBTREE; 270 op->ors_tlimit = SLAP_NO_LIMIT; 271 op->ors_slimit = SLAP_NO_LIMIT; 272 op->ors_attrs = slap_anlist_no_attrs; 273 274 op->ors_filterstr.bv_len = STRLENOF( "(&(=*)(!(=*)))" ) 275 + vn->vn_attr->ad_cname.bv_len 276 + vn->vn_vernum->ad_cname.bv_len; 277 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx ); 278 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1, 279 "(&(%s=*)(!(%s=*)))", 280 vn->vn_attr->ad_cname.bv_val, 281 vn->vn_vernum->ad_cname.bv_val ); 282 283 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val ); 284 if ( op->ors_filter == NULL ) { 285 rs.sr_err = LDAP_OTHER; 286 goto done_search; 287 } 288 289 op->o_callback = ≻ 290 sc.sc_response = vernum_repair_cb; 291 sc.sc_private = &rcb; 292 rcb.bd = &db; 293 db = *be; 294 db.bd_info = (BackendInfo *)on; 295 296 (void)op->o_bd->bd_info->bi_op_search( op, &rs ); 297 298 op->o_tag = LDAP_REQ_MODIFY; 299 sc.sc_response = slap_null_cb; 300 sc.sc_private = NULL; 301 memset( &op->oq_modify, 0, sizeof( req_modify_s ) ); 302 303 for ( rmod = rcb.mods; rmod != NULL; ) { 304 vernum_mod_t *rnext; 305 Modifications mod; 306 struct berval vals[2] = { BER_BVNULL }; 307 SlapReply rs2 = { REP_RESULT }; 308 309 mod.sml_flags = SLAP_MOD_INTERNAL; 310 mod.sml_op = LDAP_MOD_REPLACE; 311 mod.sml_desc = vn->vn_vernum; 312 mod.sml_type = vn->vn_vernum->ad_cname; 313 mod.sml_values = vals; 314 mod.sml_values[0] = val_init; 315 mod.sml_nvalues = NULL; 316 mod.sml_numvals = 1; 317 mod.sml_next = NULL; 318 319 op->o_req_dn = rmod->ndn; 320 op->o_req_ndn = rmod->ndn; 321 322 op->orm_modlist = &mod; 323 324 op->o_bd->be_modify( op, &rs2 ); 325 326 slap_mods_free( op->orm_modlist->sml_next, 1 ); 327 if ( rs2.sr_err == LDAP_SUCCESS ) { 328 Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair: entry DN=\"%s\" repaired\n", 329 op->o_log_prefix, rmod->ndn.bv_val, 0 ); 330 nrepaired++; 331 332 } else { 333 Debug( LDAP_DEBUG_ANY, "%s: vernum_repair: entry DN=\"%s\" repair failed (%d)\n", 334 op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err ); 335 } 336 337 rnext = rmod->next; 338 op->o_tmpfree( rmod, op->o_tmpmemctx ); 339 rmod = rnext; 340 } 341 342done_search:; 343 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 344 filter_free_x( op, op->ors_filter, 1 ); 345 346 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO, 347 "vernum: repaired=%d\n", nrepaired ); 348 349 return 0; 350} 351 352static int 353vernum_db_open( 354 BackendDB *be, 355 ConfigReply *cr ) 356{ 357 slap_overinst *on = (slap_overinst *)be->bd_info; 358 vernum_t *vn = (vernum_t *)on->on_bi.bi_private; 359 360 if ( SLAP_SINGLE_SHADOW( be ) ) { 361 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 362 "vernum incompatible with shadow database \"%s\".\n", 363 be->be_suffix[ 0 ].bv_val ); 364 return 1; 365 } 366 367 /* default: unicodePwd & msDS-KeyVersionNumber */ 368 if ( vn->vn_attr == NULL ) { 369 const char *text = NULL; 370 int rc; 371 372 rc = slap_str2ad( "unicodePwd", &vn->vn_attr, &text ); 373 if ( rc != LDAP_SUCCESS ) { 374 Debug( LDAP_DEBUG_ANY, "vernum: unable to find attribute 'unicodePwd' (%d: %s)\n", 375 rc, text, 0 ); 376 return 1; 377 } 378 379 vn->vn_vernum = ad_msDS_KeyVersionNumber; 380 } 381 382 return vernum_repair( be ); 383} 384 385static int 386vernum_db_destroy( 387 BackendDB *be, 388 ConfigReply *cr ) 389{ 390 slap_overinst *on = (slap_overinst *)be->bd_info; 391 vernum_t *vn = (vernum_t *)on->on_bi.bi_private; 392 393 if ( vn ) { 394 ch_free( vn ); 395 on->on_bi.bi_private = NULL; 396 } 397 398 return 0; 399} 400 401static struct { 402 char *desc; 403 AttributeDescription **adp; 404} as[] = { 405 { "( 1.2.840.113556.1.4.1782 " 406 "NAME 'msDS-KeyVersionNumber' " 407 "DESC 'in the original specification the syntax is 2.5.5.9' " 408 "SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' " 409 "EQUALITY integerMatch " 410 "SINGLE-VALUE " 411 "USAGE dSAOperation " 412 "NO-USER-MODIFICATION " 413 ")", 414 &ad_msDS_KeyVersionNumber }, 415 { NULL } 416}; 417 418int 419vernum_initialize(void) 420{ 421 int code, i; 422 423 for ( i = 0; as[ i ].desc != NULL; i++ ) { 424 code = register_at( as[ i ].desc, as[ i ].adp, 0 ); 425 if ( code ) { 426 Debug( LDAP_DEBUG_ANY, 427 "vernum_initialize: register_at #%d failed\n", 428 i, 0, 0 ); 429 return code; 430 } 431 432 /* Allow Manager to set these as needed */ 433 if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) { 434 (*as[ i ].adp)->ad_type->sat_flags |= 435 SLAP_AT_MANAGEABLE; 436 } 437 } 438 439 vernum.on_bi.bi_type = "vernum"; 440 441 vernum.on_bi.bi_op_add = vernum_op_add; 442 vernum.on_bi.bi_op_modify = vernum_op_modify; 443 444 vernum.on_bi.bi_db_init = vernum_db_init; 445 vernum.on_bi.bi_db_open = vernum_db_open; 446 vernum.on_bi.bi_db_destroy = vernum_db_destroy; 447 448 return overlay_register( &vernum ); 449} 450 451#if SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC 452int 453init_module( int argc, char *argv[] ) 454{ 455 return vernum_initialize(); 456} 457#endif /* SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC */ 458 459#endif /* SLAPD_OVER_VERNUM */ 460