1264790Sbapt/* $NetBSD: ldapsync.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */ 2264790Sbapt 3272955Srodrigc/* ldapsync.c -- LDAP Content Sync Routines */ 4264790Sbapt/* $OpenLDAP$ */ 5264790Sbapt/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6264790Sbapt * 7264790Sbapt * Copyright 2003-2021 The OpenLDAP Foundation. 8264790Sbapt * Portions Copyright 2003 IBM Corporation. 9264790Sbapt * All rights reserved. 10264790Sbapt * 11264790Sbapt * Redistribution and use in source and binary forms, with or without 12264790Sbapt * modification, are permitted only as authorized by the OpenLDAP 13264790Sbapt * Public License. 14264790Sbapt * 15264790Sbapt * 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 20#include <sys/cdefs.h> 21__RCSID("$NetBSD: ldapsync.c,v 1.3 2021/08/14 16:14:58 christos Exp $"); 22 23#include "portable.h" 24 25#include <stdio.h> 26 27#include <ac/string.h> 28#include <ac/socket.h> 29 30#include "lutil.h" 31#include "slap.h" 32#include "../../libraries/liblber/lber-int.h" /* get ber_strndup() */ 33#include "lutil_ldap.h" 34 35struct slap_sync_cookie_s slap_sync_cookie = 36 LDAP_STAILQ_HEAD_INITIALIZER( slap_sync_cookie ); 37 38void 39slap_compose_sync_cookie( 40 Operation *op, 41 struct berval *cookie, 42 BerVarray csn, 43 int rid, 44 int sid, 45 struct berval *delcsn ) 46{ 47 int len, numcsn = 0; 48 49 if ( csn ) { 50 for (; !BER_BVISNULL( &csn[numcsn] ); numcsn++); 51 } 52 53 if ( numcsn == 0 || rid == -1 ) { 54 char cookiestr[ LDAP_PVT_CSNSTR_BUFSIZE + 20 ]; 55 if ( rid == -1 ) { 56 cookiestr[0] = '\0'; 57 len = 0; 58 } else { 59 len = snprintf( cookiestr, sizeof( cookiestr ), 60 "rid=%03d", rid ); 61 if ( sid >= 0 ) { 62 len += sprintf( cookiestr+len, ",sid=%03x", sid ); 63 } 64 } 65 ber_str2bv_x( cookiestr, len, 1, cookie, 66 op ? op->o_tmpmemctx : NULL ); 67 } else { 68 char *ptr; 69 int i; 70 71 len = 0; 72 for ( i=0; i<numcsn; i++) 73 len += csn[i].bv_len + 1; 74 if ( delcsn && !BER_BVISEMPTY(delcsn) ) 75 len += STRLENOF(",delcsn=") + delcsn->bv_len; 76 77 len += STRLENOF("rid=123,csn="); 78 if ( sid >= 0 ) 79 len += STRLENOF("sid=xxx,"); 80 81 cookie->bv_val = slap_sl_malloc( len, op ? op->o_tmpmemctx : NULL ); 82 83 len = sprintf( cookie->bv_val, "rid=%03d,", rid ); 84 ptr = cookie->bv_val + len; 85 if ( sid >= 0 ) { 86 ptr += sprintf( ptr, "sid=%03x,", sid ); 87 } 88 ptr = lutil_strcopy( ptr, "csn=" ); 89 for ( i=0; i<numcsn; i++) { 90 ptr = lutil_strncopy( ptr, csn[i].bv_val, csn[i].bv_len ); 91 *ptr++ = ';'; 92 } 93 ptr--; 94 if ( delcsn && !BER_BVISEMPTY(delcsn) ) { 95 ptr = lutil_strcopy( ptr, ",delcsn=" ); 96 ptr = lutil_strncopy( ptr, delcsn->bv_val, delcsn->bv_len ); 97 } 98 *ptr = '\0'; 99 cookie->bv_len = ptr - cookie->bv_val; 100 } 101} 102 103void 104slap_sync_cookie_free( 105 struct sync_cookie *cookie, 106 int free_cookie 107) 108{ 109 if ( cookie == NULL ) 110 return; 111 112 if ( cookie->sids ) { 113 ch_free( cookie->sids ); 114 cookie->sids = NULL; 115 } 116 117 if ( cookie->ctxcsn ) { 118 ber_bvarray_free( cookie->ctxcsn ); 119 cookie->ctxcsn = NULL; 120 } 121 cookie->numcsns = 0; 122 if ( !BER_BVISNULL( &cookie->octet_str )) { 123 ch_free( cookie->octet_str.bv_val ); 124 BER_BVZERO( &cookie->octet_str ); 125 } 126 127 if ( !BER_BVISNULL( &cookie->delcsn )) { 128 ch_free( cookie->delcsn.bv_val ); 129 BER_BVZERO( &cookie->delcsn ); 130 } 131 132 if ( free_cookie ) { 133 ch_free( cookie ); 134 } 135 136 return; 137} 138 139int 140slap_parse_csn_sid( struct berval *csnp ) 141{ 142 char *p, *q; 143 struct berval csn = *csnp; 144 int i; 145 146 p = ber_bvchr( &csn, '#' ); 147 if ( !p ) 148 return -1; 149 p++; 150 csn.bv_len -= p - csn.bv_val; 151 csn.bv_val = p; 152 153 p = ber_bvchr( &csn, '#' ); 154 if ( !p ) 155 return -1; 156 p++; 157 csn.bv_len -= p - csn.bv_val; 158 csn.bv_val = p; 159 160 q = ber_bvchr( &csn, '#' ); 161 if ( !q ) 162 return -1; 163 164 csn.bv_len = q - p; 165 166 i = strtol( p, &q, 16 ); 167 if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) { 168 i = -1; 169 } 170 171 return i; 172} 173 174int * 175slap_parse_csn_sids( BerVarray csns, int numcsns, void *memctx ) 176{ 177 int i, *ret; 178 179 ret = slap_sl_malloc( numcsns * sizeof(int), memctx ); 180 for ( i=0; i<numcsns; i++ ) { 181 ret[i] = slap_parse_csn_sid( &csns[i] ); 182 } 183 return ret; 184} 185 186static slap_mr_match_func sidsort_cmp; 187 188static const MatchingRule sidsort_mr = { 189 { 0 }, 190 NULL, 191 { 0 }, 192 { 0 }, 193 0, 194 NULL, NULL, NULL, sidsort_cmp 195}; 196static const AttributeType sidsort_at = { 197 { 0 }, 198 { 0 }, 199 NULL, NULL, (MatchingRule *)&sidsort_mr, 200 NULL, NULL, NULL, NULL, NULL, NULL, NULL, SLAP_AT_SORTED_VAL 201}; 202static const AttributeDescription sidsort_ad = { 203 NULL, 204 (AttributeType *)&sidsort_at 205}; 206 207static int 208sidsort_cmp( 209 int *matchp, 210 slap_mask_t flags, 211 Syntax *syntax, 212 MatchingRule *mr, 213 struct berval *b1, 214 void *v2 ) 215{ 216 struct berval *b2 = v2; 217 *matchp = b1->bv_len - b2->bv_len; 218 return LDAP_SUCCESS; 219} 220 221/* sort CSNs by SID. Use a fake Attribute with our own 222 * syntax and matching rule, which sorts the nvals by 223 * bv_len order. Stuff our sids into the bv_len. 224 */ 225int 226slap_sort_csn_sids( BerVarray csns, int *sids, int numcsns, void *memctx ) 227{ 228 Attribute a; 229 const char *text; 230 int i, rc; 231 232 a.a_desc = (AttributeDescription *)&sidsort_ad; 233 a.a_nvals = slap_sl_malloc( numcsns * sizeof(struct berval), memctx ); 234 for ( i=0; i<numcsns; i++ ) { 235 a.a_nvals[i].bv_len = sids[i]; 236 a.a_nvals[i].bv_val = NULL; 237 } 238 a.a_vals = csns; 239 a.a_numvals = numcsns; 240 a.a_flags = 0; 241 rc = slap_sort_vals( (Modifications *)&a, &text, &i, memctx ); 242 for ( i=0; i<numcsns; i++ ) 243 sids[i] = a.a_nvals[i].bv_len; 244 slap_sl_free( a.a_nvals, memctx ); 245 return rc; 246} 247 248void 249slap_insert_csn_sids( 250 struct sync_cookie *ck, 251 int pos, 252 int sid, 253 struct berval *csn 254) 255{ 256 int i; 257 ck->numcsns++; 258 ck->ctxcsn = ch_realloc( ck->ctxcsn, 259 (ck->numcsns+1) * sizeof(struct berval)); 260 BER_BVZERO( &ck->ctxcsn[ck->numcsns] ); 261 ck->sids = ch_realloc( ck->sids, ck->numcsns * sizeof(int)); 262 for ( i = ck->numcsns-1; i > pos; i-- ) { 263 ck->ctxcsn[i] = ck->ctxcsn[i-1]; 264 ck->sids[i] = ck->sids[i-1]; 265 } 266 ck->sids[i] = sid; 267 ber_dupbv( &ck->ctxcsn[i], csn ); 268} 269 270int 271slap_parse_sync_cookie( 272 struct sync_cookie *cookie, 273 void *memctx 274) 275{ 276 char *csn_ptr; 277 char *csn_str; 278 char *cval; 279 char *next, *end; 280 AttributeDescription *ad = slap_schema.si_ad_entryCSN; 281 282 if ( cookie == NULL ) 283 return -1; 284 285 if ( cookie->octet_str.bv_len <= STRLENOF( "rid=" ) ) 286 return -1; 287 288 cookie->rid = -1; 289 cookie->sid = -1; 290 cookie->ctxcsn = NULL; 291 cookie->sids = NULL; 292 cookie->numcsns = 0; 293 BER_BVZERO( &cookie->delcsn ); 294 295 end = cookie->octet_str.bv_val + cookie->octet_str.bv_len; 296 297 for ( next=cookie->octet_str.bv_val; next < end; ) { 298 if ( !strncmp( next, "rid=", STRLENOF("rid=") )) { 299 char *rid_ptr = next; 300 cookie->rid = strtol( &rid_ptr[ STRLENOF( "rid=" ) ], &next, 10 ); 301 if ( next == rid_ptr || 302 next > end || 303 ( *next && *next != ',' ) || 304 cookie->rid < 0 || 305 cookie->rid > SLAP_SYNC_RID_MAX ) 306 { 307 return -1; 308 } 309 if ( *next == ',' ) { 310 next++; 311 } 312 if ( !ad ) { 313 break; 314 } 315 continue; 316 } 317 if ( !strncmp( next, "sid=", STRLENOF("sid=") )) { 318 char *sid_ptr = next; 319 sid_ptr = next; 320 cookie->sid = strtol( &sid_ptr[ STRLENOF( "sid=" ) ], &next, 16 ); 321 if ( next == sid_ptr || 322 next > end || 323 ( *next && *next != ',' ) || 324 cookie->sid < 0 || 325 cookie->sid > SLAP_SYNC_SID_MAX ) 326 { 327 return -1; 328 } 329 if ( *next == ',' ) { 330 next++; 331 } 332 continue; 333 } 334 if ( !strncmp( next, "csn=", STRLENOF("csn=") )) { 335 struct berval stamp; 336 337 next += STRLENOF("csn="); 338 while ( next < end ) { 339 csn_str = next; 340 csn_ptr = strchr( csn_str, '#' ); 341 if ( !csn_ptr || csn_ptr > end ) 342 break; 343 /* ad will be NULL when called from main. we just 344 * want to parse the rid then. But we still iterate 345 * through the string to find the end. 346 */ 347 cval = strchr( csn_ptr, ';' ); 348 if ( !cval ) 349 cval = strchr(csn_ptr, ',' ); 350 if ( cval ) 351 stamp.bv_len = cval - csn_str; 352 else 353 stamp.bv_len = end - csn_str; 354 if ( ad ) { 355 struct berval bv; 356 stamp.bv_val = csn_str; 357 if ( ad->ad_type->sat_syntax->ssyn_validate( 358 ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS ) 359 break; 360 if ( ad->ad_type->sat_equality->smr_normalize( 361 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, 362 ad->ad_type->sat_syntax, 363 ad->ad_type->sat_equality, 364 &stamp, &bv, memctx ) != LDAP_SUCCESS ) 365 break; 366 ber_bvarray_add_x( &cookie->ctxcsn, &bv, memctx ); 367 cookie->numcsns++; 368 } 369 if ( cval ) { 370 next = cval + 1; 371 if ( *cval != ';' ) 372 break; 373 } else { 374 next = end; 375 break; 376 } 377 } 378 continue; 379 } 380 if ( !strncmp( next, "delcsn=", STRLENOF("delcsn=") )) { 381 struct berval stamp; 382 383 next += STRLENOF("delcsn="); 384 while ( next < end ) { 385 csn_str = next; 386 csn_ptr = strchr( csn_str, '#' ); 387 if ( !csn_ptr || csn_ptr > end ) 388 break; 389 /* ad will be NULL when called from main. we just 390 * want to parse the rid then. But we still iterate 391 * through the string to find the end. 392 */ 393 cval = strchr( csn_ptr, ';' ); 394 if ( !cval ) 395 cval = strchr(csn_ptr, ',' ); 396 if ( cval ) 397 stamp.bv_len = cval - csn_str; 398 else 399 stamp.bv_len = end - csn_str; 400 if ( ad ) { 401 struct berval bv; 402 stamp.bv_val = csn_str; 403 if ( ad->ad_type->sat_syntax->ssyn_validate( 404 ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS ) 405 break; 406 if ( ad->ad_type->sat_equality->smr_normalize( 407 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, 408 ad->ad_type->sat_syntax, 409 ad->ad_type->sat_equality, 410 &stamp, &bv, memctx ) != LDAP_SUCCESS ) 411 break; 412 cookie->delcsn = bv; 413 } 414 if ( cval ) { 415 next = cval + 1; 416 } else { 417 next = end; 418 } 419 break; 420 } 421 continue; 422 } 423 next++; 424 } 425 if ( cookie->numcsns ) { 426 cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns, 427 memctx ); 428 if ( cookie->numcsns > 1 ) 429 slap_sort_csn_sids( cookie->ctxcsn, cookie->sids, cookie->numcsns, memctx ); 430 } 431 return 0; 432} 433 434/* count the numcsns and regenerate the list of SIDs in a recomposed cookie */ 435void 436slap_reparse_sync_cookie( 437 struct sync_cookie *cookie, 438 void *memctx ) 439{ 440 if ( cookie->ctxcsn ) { 441 for (; !BER_BVISNULL( &cookie->ctxcsn[cookie->numcsns] ); cookie->numcsns++); 442 } 443 if ( cookie->numcsns ) { 444 cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns, NULL ); 445 if ( cookie->numcsns > 1 ) 446 slap_sort_csn_sids( cookie->ctxcsn, cookie->sids, cookie->numcsns, memctx ); 447 } 448} 449 450int 451slap_init_sync_cookie_ctxcsn( 452 struct sync_cookie *cookie 453) 454{ 455 char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE + 4 ]; 456 struct berval octet_str = BER_BVNULL; 457 struct berval ctxcsn = BER_BVNULL; 458 459 if ( cookie == NULL ) 460 return -1; 461 462 octet_str.bv_len = snprintf( csnbuf, LDAP_PVT_CSNSTR_BUFSIZE + 4, 463 "csn=%4d%02d%02d%02d%02d%02dZ#%06x#%02x#%06x", 464 1900, 1, 1, 0, 0, 0, 0, 0, 0 ); 465 octet_str.bv_val = csnbuf; 466 ch_free( cookie->octet_str.bv_val ); 467 ber_dupbv( &cookie->octet_str, &octet_str ); 468 469 ctxcsn.bv_val = octet_str.bv_val + 4; 470 ctxcsn.bv_len = octet_str.bv_len - 4; 471 cookie->ctxcsn = NULL; 472 value_add_one( &cookie->ctxcsn, &ctxcsn ); 473 cookie->numcsns = 1; 474 cookie->sid = -1; 475 BER_BVZERO( &cookie->delcsn ); 476 477 return 0; 478} 479 480struct sync_cookie * 481slap_dup_sync_cookie( 482 struct sync_cookie *dst, 483 struct sync_cookie *src 484) 485{ 486 struct sync_cookie *new; 487 int i; 488 489 if ( src == NULL ) 490 return NULL; 491 492 if ( dst ) { 493 ber_bvarray_free( dst->ctxcsn ); 494 dst->ctxcsn = NULL; 495 dst->sids = NULL; 496 ch_free( dst->octet_str.bv_val ); 497 BER_BVZERO( &dst->octet_str ); 498 new = dst; 499 } else { 500 new = ( struct sync_cookie * ) 501 ch_calloc( 1, sizeof( struct sync_cookie )); 502 } 503 504 new->rid = src->rid; 505 new->sid = src->sid; 506 new->numcsns = src->numcsns; 507 508 if ( src->numcsns ) { 509 if ( ber_bvarray_dup_x( &new->ctxcsn, src->ctxcsn, NULL )) { 510 if ( !dst ) { 511 ch_free( new ); 512 } 513 return NULL; 514 } 515 new->sids = ch_malloc( src->numcsns * sizeof(int) ); 516 for (i=0; i<src->numcsns; i++) 517 new->sids[i] = src->sids[i]; 518 } 519 520 if ( !BER_BVISNULL( &src->delcsn )) { 521 ber_dupbv( &new->delcsn, &src->delcsn ); 522 } 523 524 if ( !BER_BVISNULL( &src->octet_str )) { 525 ber_dupbv( &new->octet_str, &src->octet_str ); 526 } 527 528 return new; 529} 530 531