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