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