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