1/* $NetBSD: usn.c,v 1.2 2021/08/14 16:14:54 christos Exp $ */ 2 3/* usn.c - Maintain Microsoft-style Update Sequence Numbers */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2007-2021 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 Howard Chu for inclusion in 20 * OpenLDAP Software. 21 */ 22 23#include <sys/cdefs.h> 24__RCSID("$NetBSD: usn.c,v 1.2 2021/08/14 16:14:54 christos Exp $"); 25 26#include "portable.h" 27 28#ifdef SLAPD_OVER_USN 29 30#include <stdio.h> 31 32#include <ac/string.h> 33#include <ac/socket.h> 34 35#include "slap.h" 36#include "slap-config.h" 37 38/* This overlay intercepts write operations and adds a Microsoft-style 39 * USN to the target entry. 40 */ 41 42typedef struct usn_info { 43 int ui_current; 44 ldap_pvt_thread_mutex_t ui_mutex; 45} usn_info_t; 46 47static AttributeDescription *ad_usnCreated, *ad_usnChanged; 48 49static struct { 50 char *desc; 51 AttributeDescription **adp; 52} as[] = { 53 { "( 1.2.840.113556.1.2.19 " 54 "NAME 'uSNCreated' " 55 "SYNTAX '1.2.840.113556.1.4.906' " 56 "SINGLE-VALUE " 57 "NO-USER-MODIFICATION )", 58 &ad_usnCreated }, 59 { "( 1.2.840.113556.1.2.120 " 60 "NAME 'uSNChanged' " 61 "SYNTAX '1.2.840.113556.1.4.906' " 62 "SINGLE-VALUE " 63 "NO-USER-MODIFICATION )", 64 &ad_usnChanged }, 65 { NULL } 66}; 67 68static int 69usn_func( Operation *op, SlapReply *rs ) 70{ 71 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 72 usn_info_t *ui = on->on_bi.bi_private; 73 int my_usn; 74 char intbuf[64]; 75 struct berval bv[2]; 76 77 ldap_pvt_thread_mutex_lock( &ui->ui_mutex ); 78 ui->ui_current++; 79 my_usn = ui->ui_current; 80 ldap_pvt_thread_mutex_unlock( &ui->ui_mutex ); 81 82 BER_BVZERO(&bv[1]); 83 bv[0].bv_val = intbuf; 84 bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn ); 85 switch(op->o_tag) { 86 case LDAP_REQ_ADD: 87 attr_merge( op->ora_e, ad_usnCreated, bv, NULL ); 88 attr_merge( op->ora_e, ad_usnChanged, bv, NULL ); 89 break; 90 case LDAP_REQ_DELETE: 91 /* Probably need to update root usnLastObjRem */ 92 break; 93 default: { 94 /* Modify, ModDN */ 95 Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 ); 96 for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next ); 97 ml->sml_next = mod; 98 mod->sml_desc = ad_usnChanged; 99 mod->sml_numvals = 1; 100 value_add_one( &mod->sml_values, &bv[0] ); 101 mod->sml_nvalues = NULL; 102 mod->sml_op = LDAP_MOD_REPLACE; 103 mod->sml_flags = 0; 104 mod->sml_next = NULL; 105 break; 106 } 107 } 108 return SLAP_CB_CONTINUE; 109} 110 111static int 112usn_operational( 113 Operation *op, 114 SlapReply *rs ) 115{ 116 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 117 usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private; 118 119 if ( rs->sr_entry && 120 dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) { 121 122 if ( SLAP_OPATTRS( rs->sr_attr_flags ) || 123 ad_inlist( ad_usnChanged, rs->sr_attrs )) { 124 Attribute *a, **ap = NULL; 125 char intbuf[64]; 126 struct berval bv; 127 int my_usn; 128 129 for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) { 130 if ( a->a_desc == ad_usnChanged ) 131 break; 132 } 133 134 if ( !a ) { 135 for ( ap = &rs->sr_operational_attrs; *ap; 136 ap=&(*ap)->a_next ); 137 138 a = attr_alloc( ad_usnChanged ); 139 *ap = a; 140 } 141 142 if ( !ap ) { 143 if ( rs_entry2modifiable( op,rs, on )) { 144 a = attr_find( rs->sr_entry->e_attrs, 145 ad_usnChanged ); 146 } 147 ber_bvarray_free( a->a_vals ); 148 a->a_vals = NULL; 149 a->a_numvals = 0; 150 } 151 ldap_pvt_thread_mutex_lock( &ui->ui_mutex ); 152 my_usn = ui->ui_current; 153 ldap_pvt_thread_mutex_unlock( &ui->ui_mutex ); 154 bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn ); 155 bv.bv_val = intbuf; 156 attr_valadd( a, &bv, NULL, 1 ); 157 } 158 } 159 return SLAP_CB_CONTINUE; 160} 161 162/* Read the old USN from the underlying DB. This code is 163 * stolen from the syncprov overlay. 164 */ 165static int 166usn_db_open( 167 BackendDB *be, 168 ConfigReply *cr) 169{ 170 slap_overinst *on = (slap_overinst *) be->bd_info; 171 usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private; 172 173 Connection conn = { 0 }; 174 OperationBuffer opbuf; 175 Operation *op; 176 Entry *e = NULL; 177 Attribute *a; 178 int rc; 179 void *thrctx = NULL; 180 181 thrctx = ldap_pvt_thread_pool_context(); 182 connection_fake_init( &conn, &opbuf, thrctx ); 183 op = &opbuf.ob_op; 184 op->o_bd = be; 185 op->o_dn = be->be_rootdn; 186 op->o_ndn = be->be_rootndn; 187 188 rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL, 189 slap_schema.si_ad_contextCSN, 0, &e, on ); 190 191 if ( e ) { 192 a = attr_find( e->e_attrs, ad_usnChanged ); 193 if ( a ) { 194 ui->ui_current = atoi( a->a_vals[0].bv_val ); 195 } 196 overlay_entry_release_ov( op, e, 0, on ); 197 } 198 return 0; 199} 200 201static int 202usn_db_init( 203 BackendDB *be, 204 ConfigReply *cr 205) 206{ 207 slap_overinst *on = (slap_overinst *)be->bd_info; 208 usn_info_t *ui; 209 210 if ( SLAP_ISGLOBALOVERLAY( be ) ) { 211 Debug( LDAP_DEBUG_ANY, 212 "usn must be instantiated within a database.\n" ); 213 return 1; 214 } 215 216 ui = ch_calloc(1, sizeof(usn_info_t)); 217 ldap_pvt_thread_mutex_init( &ui->ui_mutex ); 218 on->on_bi.bi_private = ui; 219 return 0; 220} 221 222static int 223usn_db_close( 224 BackendDB *be, 225 ConfigReply *cr 226) 227{ 228 slap_overinst *on = (slap_overinst *)be->bd_info; 229 usn_info_t *ui = on->on_bi.bi_private; 230 Connection conn = {0}; 231 OperationBuffer opbuf; 232 Operation *op; 233 SlapReply rs = {REP_RESULT}; 234 void *thrctx; 235 236 Modifications mod; 237 slap_callback cb = {0}; 238 char intbuf[64]; 239 struct berval bv[2]; 240 241 thrctx = ldap_pvt_thread_pool_context(); 242 connection_fake_init( &conn, &opbuf, thrctx ); 243 op = &opbuf.ob_op; 244 op->o_bd = be; 245 BER_BVZERO( &bv[1] ); 246 bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current ); 247 bv[0].bv_val = intbuf; 248 mod.sml_numvals = 1; 249 mod.sml_values = bv; 250 mod.sml_nvalues = NULL; 251 mod.sml_desc = ad_usnChanged; 252 mod.sml_op = LDAP_MOD_REPLACE; 253 mod.sml_flags = 0; 254 mod.sml_next = NULL; 255 256 cb.sc_response = slap_null_cb; 257 op->o_tag = LDAP_REQ_MODIFY; 258 op->o_callback = &cb; 259 op->orm_modlist = &mod; 260 op->orm_no_opattrs = 1; 261 op->o_dn = be->be_rootdn; 262 op->o_ndn = be->be_rootndn; 263 op->o_req_dn = op->o_bd->be_suffix[0]; 264 op->o_req_ndn = op->o_bd->be_nsuffix[0]; 265 op->o_bd->bd_info = on->on_info->oi_orig; 266 op->o_managedsait = SLAP_CONTROL_NONCRITICAL; 267 op->o_no_schema_check = 1; 268 op->o_bd->be_modify( op, &rs ); 269 if ( mod.sml_next != NULL ) { 270 slap_mods_free( mod.sml_next, 1 ); 271 } 272 return 0; 273} 274 275static int 276usn_db_destroy( 277 BackendDB *be, 278 ConfigReply *cr 279) 280{ 281 slap_overinst *on = (slap_overinst *)be->bd_info; 282 usn_info_t *ui = on->on_bi.bi_private; 283 284 ldap_pvt_thread_mutex_destroy( &ui->ui_mutex ); 285 ch_free( ui ); 286 on->on_bi.bi_private = NULL; 287 return 0; 288} 289 290/* This overlay is set up for dynamic loading via moduleload. For static 291 * configuration, you'll need to arrange for the slap_overinst to be 292 * initialized and registered by some other function inside slapd. 293 */ 294 295static slap_overinst usn; 296 297int 298usn_init( void ) 299{ 300 int i, code; 301 302 memset( &usn, 0, sizeof( slap_overinst ) ); 303 usn.on_bi.bi_type = "usn"; 304 usn.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 305 usn.on_bi.bi_db_init = usn_db_init; 306 usn.on_bi.bi_db_destroy = usn_db_destroy; 307 usn.on_bi.bi_db_open = usn_db_open; 308 usn.on_bi.bi_db_close = usn_db_close; 309 310 usn.on_bi.bi_op_modify = usn_func; 311 usn.on_bi.bi_op_modrdn = usn_func; 312 usn.on_bi.bi_op_add = usn_func; 313 usn.on_bi.bi_op_delete = usn_func; 314 usn.on_bi.bi_operational = usn_operational; 315 316 for ( i = 0; as[i].desc; i++ ) { 317 code = register_at( as[i].desc, as[i].adp, 0 ); 318 if ( code ) { 319 Debug( LDAP_DEBUG_ANY, 320 "usn_init: register_at #%d failed\n", i ); 321 return code; 322 } 323 } 324 return overlay_register( &usn ); 325} 326 327#if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC 328int 329init_module( int argc, char *argv[] ) 330{ 331 return usn_init(); 332} 333#endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */ 334 335#endif /* defined(SLAPD_OVER_USN) */ 336