translucent.c 30.8 KB
Newer Older
1
2
3
4
/* translucent.c - translucent proxy module */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2004-2008 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 * Portions Copyright 2005 Symas Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Symas Corp. for inclusion in
 * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20
21
22
23
24
25
26
27
28
29
30
31
 */

#include "portable.h"

#ifdef SLAPD_OVER_TRANSLUCENT

#include <stdio.h>

#include <ac/string.h>
#include <ac/socket.h>

#include "slap.h"
32
#include "lutil.h"
33

Howard Chu's avatar
Howard Chu committed
34
35
#include "config.h"

36
/* config block */
Howard Chu's avatar
Howard Chu committed
37
typedef struct translucent_info {
Howard Chu's avatar
Howard Chu committed
38
	BackendDB db;			/* captive backend */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
39
40
	AttributeName *local;	/* valid attrs for local filters */
	AttributeName *remote;	/* valid attrs for remote filters */
Howard Chu's avatar
Howard Chu committed
41
	int strict;
Howard Chu's avatar
Howard Chu committed
42
	int no_glue;
Howard Chu's avatar
Howard Chu committed
43
	int defer_db_open;
Howard Chu's avatar
Howard Chu committed
44
} translucent_info;
45

Howard Chu's avatar
Howard Chu committed
46
47
48
static ConfigLDAPadd translucent_ldadd;
static ConfigCfAdd translucent_cfadd;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
49
50
51
52
53
54
55
static ConfigDriver translucent_cf_gen;

enum {
	TRANS_LOCAL = 1,
	TRANS_REMOTE
};

Howard Chu's avatar
Howard Chu committed
56
57
58
59
60
61
62
63
64
65
66
67
68
static ConfigTable translucentcfg[] = {
	{ "translucent_strict", "on|off", 1, 2, 0,
	  ARG_ON_OFF|ARG_OFFSET,
	  (void *)offsetof(translucent_info, strict),
	  "( OLcfgOvAt:14.1 NAME 'olcTranslucentStrict' "
	  "DESC 'Reveal attribute deletion constraint violations' "
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
	{ "translucent_no_glue", "on|off", 1, 2, 0,
	  ARG_ON_OFF|ARG_OFFSET,
	  (void *)offsetof(translucent_info, no_glue),
	  "( OLcfgOvAt:14.2 NAME 'olcTranslucentNoGlue' "
	  "DESC 'Disable automatic glue records for ADD and MODRDN' "
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
69
70
71
72
73
74
75
76
77
78
79
80
	{ "translucent_local", "attr[,attr...]", 1, 2, 0,
	  ARG_STRING|ARG_MAGIC|TRANS_LOCAL,
	  translucent_cf_gen,
	  "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' "
	  "DESC 'Attributes to use in local search filter' "
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
	{ "translucent_remote", "attr[,attr...]", 1, 2, 0,
	  ARG_STRING|ARG_MAGIC|TRANS_REMOTE,
	  translucent_cf_gen,
	  "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
	  "DESC 'Attributes to use in remote search filter' "
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
81
82
83
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

Howard Chu's avatar
Howard Chu committed
84
85
86
87
88
89
90
91
static ConfigTable transdummy[] = {
	{ "", "", 0, 0, 0, ARG_IGNORED,
		NULL, "( OLcfgGlAt:13 NAME 'olcDatabase' "
			"DESC 'The backend type for a database instance' "
			"SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

Howard Chu's avatar
Howard Chu committed
92
93
94
95
96
static ConfigOCs translucentocs[] = {
	{ "( OLcfgOvOc:14.1 "
	  "NAME 'olcTranslucentConfig' "
	  "DESC 'Translucent configuration' "
	  "SUP olcOverlayConfig "
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
97
98
	  "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue $"
	  " olcTranslucentLocal $ olcTranslucentRemote ) )",
Howard Chu's avatar
Howard Chu committed
99
100
101
102
	  Cft_Overlay, translucentcfg, NULL, translucent_cfadd },
	{ "( OLcfgOvOc:14.2 "
	  "NAME 'olcTranslucentDatabase' "
	  "DESC 'Translucent target database configuration' "
Howard Chu's avatar
Howard Chu committed
103
	  "AUXILIARY )", Cft_Misc, transdummy, translucent_ldadd },
Howard Chu's avatar
Howard Chu committed
104
105
	{ NULL, 0, NULL }
};
106
107
/* for translucent_init() */

Howard Chu's avatar
Howard Chu committed
108
109
110
static int
translucent_ldadd_cleanup( ConfigArgs *ca )
{
Quanah Gibson-Mount's avatar
Cleanup    
Quanah Gibson-Mount committed
111
	slap_overinst *on = ca->ca_private;
Howard Chu's avatar
Howard Chu committed
112
113
114
115
116
117
	translucent_info *ov = on->on_bi.bi_private;

	ov->defer_db_open = 0;
	return backend_startup_one( ca->be, &ca->reply );
}

Howard Chu's avatar
Howard Chu committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
static int
translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
{
	slap_overinst *on;
	translucent_info *ov;

	Debug(LDAP_DEBUG_TRACE, "==> translucent_ldadd\n", 0, 0, 0);

	if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
	     cei->ce_bi->bi_cf_ocs != translucentocs )
		return LDAP_CONSTRAINT_VIOLATION;

	on = (slap_overinst *)cei->ce_bi;
	ov = on->on_bi.bi_private;
	ca->be = &ov->db;
Quanah Gibson-Mount's avatar
Cleanup    
Quanah Gibson-Mount committed
133
	ca->ca_private = on;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
134
135
136
137
138
	if ( CONFIG_ONLINE_ADD( ca ))
		ca->cleanup = translucent_ldadd_cleanup;
	else
		ov->defer_db_open = 0;

Howard Chu's avatar
Howard Chu committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
	return LDAP_SUCCESS;
}

static int
translucent_cfadd( Operation *op, SlapReply *rs, Entry *e, ConfigArgs *ca )
{
	CfEntryInfo *cei = e->e_private;
	slap_overinst *on = (slap_overinst *)cei->ce_bi;
	translucent_info *ov = on->on_bi.bi_private;
	struct berval bv;

	Debug(LDAP_DEBUG_TRACE, "==> translucent_cfadd\n", 0, 0, 0);

	/* FIXME: should not hardcode "olcDatabase" here */
153
154
155
156
157
	bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
		"olcDatabase=%s", ov->db.bd_info->bi_type );
	if ( bv.bv_len < 0 || bv.bv_len >= sizeof( ca->cr_msg ) ) {
		return -1;
	}
158
	bv.bv_val = ca->cr_msg;
Howard Chu's avatar
Howard Chu committed
159
	ca->be = &ov->db;
Howard Chu's avatar
Howard Chu committed
160
	ov->defer_db_open = 0;
Howard Chu's avatar
Howard Chu committed
161
162
163
164
165
166
167
168
169
170
171

	/* We can only create this entry if the database is table-driven
	 */
	if ( ov->db.bd_info->bi_cf_ocs )
		config_build_entry( op, rs, cei, ca, &bv,
				    ov->db.bd_info->bi_cf_ocs,
				    &translucentocs[1] );

	return 0;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
static int
translucent_cf_gen( ConfigArgs *c )
{
	slap_overinst	*on = (slap_overinst *)c->bi;
	translucent_info *ov = on->on_bi.bi_private;
	AttributeName **an, *a2;
	int i;

	if ( c->type == TRANS_LOCAL )
		an = &ov->local;
	else
		an = &ov->remote;

	if ( c->op == SLAP_CONFIG_EMIT ) {
		if ( !*an )
			return 1;
		for ( i = 0; !BER_BVISNULL(&(*an)[i].an_name); i++ ) {
			value_add_one( &c->rvalue_vals, &(*an)[i].an_name );
		}
		return ( i < 1 );
	} else if ( c->op == LDAP_MOD_DELETE ) {
		if ( c->valx < 0 ) {
			anlist_free( *an, 1, NULL );
			*an = NULL;
		} else {
			i = c->valx;
			ch_free( (*an)[i].an_name.bv_val );
			do {
				(*an)[i] = (*an)[i+1];
			} while ( !BER_BVISNULL( &(*an)[i].an_name ));
		}
		return 0;
	}
	a2 = str2anlist( *an, c->argv[1], "," );
	if ( !a2 ) {
		snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse attribute %s",
			c->argv[0], c->argv[1] );
		Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
			"%s: %s\n", c->log, c->cr_msg, 0 );
		return ARG_BAD_CONF;
	}
	*an = a2;
	return 0;
}

217
218
219
220
221
222
223
224
225
226
227
228
229
static slap_overinst translucent;

/*
** glue_parent()
**	call syncrepl_add_glue() with the parent suffix;
**
*/

static struct berval glue[] = { BER_BVC("top"), BER_BVC("glue"), BER_BVNULL };

void glue_parent(Operation *op) {
	Operation nop = *op;
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
230
	struct berval ndn = BER_BVNULL;
231
232
	Attribute *a;
	Entry *e;
233
234
235
	struct berval	pdn;

	dnParent( &op->o_req_ndn, &pdn );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
236
	ber_dupbv_x( &ndn, &pdn, op->o_tmpmemctx );
237

Pierangelo Masarati's avatar
Pierangelo Masarati committed
238
	Debug(LDAP_DEBUG_TRACE, "=> glue_parent: fabricating glue for <%s>\n", ndn.bv_val, 0, 0);
239

240
	e = entry_alloc();
241
	e->e_id = NOID;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
242
243
	ber_dupbv(&e->e_name, &ndn);
	ber_dupbv(&e->e_nname, &ndn);
244

245
	a = attr_alloc( slap_schema.si_ad_objectClass );
246
	a->a_numvals = 2;
247
248
249
250
251
252
253
254
	a->a_vals = ch_malloc(sizeof(struct berval) * 3);
	ber_dupbv(&a->a_vals[0], &glue[0]);
	ber_dupbv(&a->a_vals[1], &glue[1]);
	ber_dupbv(&a->a_vals[2], &glue[2]);
	a->a_nvals = a->a_vals;
	a->a_next = e->e_attrs;
	e->e_attrs = a;

255
	a = attr_alloc( slap_schema.si_ad_structuralObjectClass );
256
	a->a_numvals = 1;
257
258
259
260
261
262
263
	a->a_vals = ch_malloc(sizeof(struct berval) * 2);
	ber_dupbv(&a->a_vals[0], &glue[1]);
	ber_dupbv(&a->a_vals[1], &glue[2]);
	a->a_nvals = a->a_vals;
	a->a_next = e->e_attrs;
	e->e_attrs = a;

Pierangelo Masarati's avatar
Pierangelo Masarati committed
264
265
	nop.o_req_dn = ndn;
	nop.o_req_ndn = ndn;
266
267
	nop.ora_e = e;

Howard Chu's avatar
Howard Chu committed
268
	nop.o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
269
	syncrepl_add_glue(&nop, e);
Howard Chu's avatar
Howard Chu committed
270
	nop.o_bd->bd_info = (BackendInfo *) on;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
271
272
273

	op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
	return;
}

/*
** dup_bervarray()
**	copy a BerVarray;
*/

BerVarray dup_bervarray(BerVarray b) {
	int i, len;
	BerVarray nb;
	for(len = 0; b[len].bv_val; len++);
	nb = ch_malloc((len+1) * sizeof(BerValue));
	for(i = 0; i < len; i++) ber_dupbv(&nb[i], &b[i]);
	nb[len].bv_val = NULL;
	nb[len].bv_len = 0;
	return(nb);
}

/*
** free_attr_chain()
**	free only the Attribute*, not the contents;
**
*/
298
299
300
301
302
void free_attr_chain(Attribute *b) {
	Attribute *a;
	for(a=b; a; a=a->a_next) {
		a->a_vals = NULL;
		a->a_nvals = NULL;
303
	}
304
	attrs_free( b );
305
306
307
308
309
310
	return;
}

/*
** translucent_add()
**	if not bound as root, send ACCESS error;
Howard Chu's avatar
Howard Chu committed
311
**	if glue, glue_parent();
312
313
314
315
316
317
**	return CONTINUE;
**
*/

static int translucent_add(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
318
	translucent_info *ov = on->on_bi.bi_private;
319
320
321
322
323
324
	Debug(LDAP_DEBUG_TRACE, "==> translucent_add: %s\n",
		op->o_req_dn.bv_val, 0, 0);
	if(!be_isroot(op)) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
			"user modification of overlay database not permitted");
Howard Chu's avatar
Howard Chu committed
325
		op->o_bd->bd_info = (BackendInfo *) on;
326
327
		return(rs->sr_err);
	}
Howard Chu's avatar
Howard Chu committed
328
	if(!ov->no_glue) glue_parent(op);
329
330
331
332
333
334
	return(SLAP_CB_CONTINUE);
}

/*
** translucent_modrdn()
**	if not bound as root, send ACCESS error;
Howard Chu's avatar
Howard Chu committed
335
**	if !glue, glue_parent();
336
337
338
339
340
341
**	else return CONTINUE;
**
*/

static int translucent_modrdn(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
342
	translucent_info *ov = on->on_bi.bi_private;
343
344
345
346
347
348
	Debug(LDAP_DEBUG_TRACE, "==> translucent_modrdn: %s -> %s\n",
		op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
	if(!be_isroot(op)) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
			"user modification of overlay database not permitted");
Howard Chu's avatar
Howard Chu committed
349
		op->o_bd->bd_info = (BackendInfo *) on;
350
351
		return(rs->sr_err);
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
352
353
354
355
356
	if(!ov->no_glue) {
		op->o_tag = LDAP_REQ_ADD;
		glue_parent(op);
		op->o_tag = LDAP_REQ_MODRDN;
	}
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
	return(SLAP_CB_CONTINUE);
}

/*
** translucent_delete()
**	if not bound as root, send ACCESS error;
**	else return CONTINUE;
**
*/

static int translucent_delete(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	Debug(LDAP_DEBUG_TRACE, "==> translucent_delete: %s\n",
		op->o_req_dn.bv_val, 0, 0);
	if(!be_isroot(op)) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
			"user modification of overlay database not permitted");
Howard Chu's avatar
Howard Chu committed
375
		op->o_bd->bd_info = (BackendInfo *) on;
376
377
378
379
380
		return(rs->sr_err);
	}
	return(SLAP_CB_CONTINUE);
}

381
382
383
static int
translucent_tag_cb( Operation *op, SlapReply *rs )
{
Howard Chu's avatar
Howard Chu committed
384
385
	op->o_tag = LDAP_REQ_MODIFY;
	op->orm_modlist = op->o_callback->sc_private;
386
387
388
389
390
	rs->sr_tag = slap_req2res( op->o_tag );

	return SLAP_CB_CONTINUE;
}

391
392
393
394
395
396
397
398
399
400
401
402
/*
** translucent_modify()
**	modify in local backend if exists in both;
**	otherwise, add to local backend;
**	fail if not defined in captive backend;
**
*/

static int translucent_modify(Operation *op, SlapReply *rs) {
	SlapReply nrs = { REP_RESULT };

	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
403
	translucent_info *ov = on->on_bi.bi_private;
404
	Entry *e = NULL, *re = NULL;
405
	Attribute *a, *ax;
Howard Chu's avatar
Howard Chu committed
406
	Modifications *m, **mm;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
407
	BackendDB *db;
408
	int del, rc, erc = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
409
	slap_callback cb = { 0 };
410
411
412
413

	Debug(LDAP_DEBUG_TRACE, "==> translucent_modify: %s\n",
		op->o_req_dn.bv_val, 0, 0);

Howard Chu's avatar
Howard Chu committed
414
415
416
417
418
	if(ov->defer_db_open) {
		send_ldap_error(op, rs, LDAP_UNAVAILABLE,
			"remote DB not available");
		return(rs->sr_err);
	}
419
420
421
422
423
424
425
/*
** fetch entry from the captive backend;
** if it did not exist, fail;
** release it, if captive backend supports this;
**
*/

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
426
427
428
	db = op->o_bd;
	op->o_bd = &ov->db;
	rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
Pierangelo Masarati's avatar
Pierangelo Masarati committed
429
	if(rc != LDAP_SUCCESS || re == NULL ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
430
		send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
431
432
433
			"attempt to modify nonexistent local record");
		return(rs->sr_err);
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
434
	op->o_bd = db;
435
436
437
438
439
440
441
442
443
444
445
446
447
/*
** fetch entry from local backend;
** if it exists:
**	foreach Modification:
**	    if attr not present in local:
**		if Mod == LDAP_MOD_DELETE:
**		    if remote attr not present, return NO_SUCH;
**		    if remote attr present, drop this Mod;
**		else force this Mod to LDAP_MOD_ADD;
**	return CONTINUE;
**
*/

Howard Chu's avatar
Howard Chu committed
448
	op->o_bd->bd_info = (BackendInfo *) on->on_info;
449
	rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
Howard Chu's avatar
Howard Chu committed
450
	op->o_bd->bd_info = (BackendInfo *) on;
451
452
453

	if(e && rc == LDAP_SUCCESS) {
		Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: found local entry\n", 0, 0, 0);
Howard Chu's avatar
Howard Chu committed
454
455
		for(mm = &op->orm_modlist; *mm; ) {
			m = *mm;
456
457
			for(a = e->e_attrs; a; a = a->a_next)
				if(a->a_desc == m->sml_desc) break;
Howard Chu's avatar
Howard Chu committed
458
459
460
461
			if(a) {
				mm = &m->sml_next;
				continue;		/* found local attr */
			}
462
463
464
465
466
467
468
469
			if(m->sml_op == LDAP_MOD_DELETE) {
				for(a = re->e_attrs; a; a = a->a_next)
					if(a->a_desc == m->sml_desc) break;
				/* not found remote attr */
				if(!a) {
					erc = LDAP_NO_SUCH_ATTRIBUTE;
					goto release;
				}
Howard Chu's avatar
Howard Chu committed
470
				if(ov->strict) {
471
472
473
474
475
476
					erc = LDAP_CONSTRAINT_VIOLATION;
					goto release;
				}
				Debug(LDAP_DEBUG_TRACE,
					"=> translucent_modify: silently dropping delete: %s\n",
					m->sml_desc->ad_cname.bv_val, 0, 0);
Howard Chu's avatar
Howard Chu committed
477
				*mm = m->sml_next;
Howard Chu's avatar
Howard Chu committed
478
479
480
				m->sml_next = NULL;
				slap_mods_free(m, 1);
				continue;
481
482
			}
			m->sml_op = LDAP_MOD_ADD;
Howard Chu's avatar
Howard Chu committed
483
			mm = &m->sml_next;
484
485
486
487
		}
		erc = SLAP_CB_CONTINUE;
release:
		if(re) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
488
489
490
491
492
			if(ov->db.bd_info->bi_entry_release_rw) {
				op->o_bd = &ov->db;
				ov->db.bd_info->bi_entry_release_rw(op, re, 0);
				op->o_bd = db;
			} else
493
494
				entry_free(re);
		}
Howard Chu's avatar
Howard Chu committed
495
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
496
		be_entry_release_r(op, e);
Howard Chu's avatar
Howard Chu committed
497
		op->o_bd->bd_info = (BackendInfo *) on;
498
499
500
501
502
503
504
505
506
		if(erc == SLAP_CB_CONTINUE) {
			return(erc);
		} else if(erc) {
			send_ldap_error(op, rs, erc,
				"attempt to delete nonexistent attribute");
			return(erc);
		}
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
507
508
	/* don't leak remote entry copy */
	if(re) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
509
510
511
512
513
		if(ov->db.bd_info->bi_entry_release_rw) {
			op->o_bd = &ov->db;
			ov->db.bd_info->bi_entry_release_rw(op, re, 0);
			op->o_bd = db;
		} else
Pierangelo Masarati's avatar
Pierangelo Masarati committed
514
515
			entry_free(re);
	}
516
517
518
519
/*
** foreach Modification:
**	if MOD_ADD or MOD_REPLACE, add Attribute;
** if no Modifications were suitable:
Howard Chu's avatar
Howard Chu committed
520
**	if strict, throw CONSTRAINT_VIOLATION;
521
522
523
524
525
526
527
528
529
530
**	else, return early SUCCESS;
** fabricate Entry with new Attribute chain;
** glue_parent() for this Entry;
** call bi_op_add() in local backend;
**
*/

	Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: fabricating local add\n", 0, 0, 0);
	a = NULL;
	for(del = 0, ax = NULL, m = op->orm_modlist; m; m = m->sml_next) {
531
		Attribute atmp;
532
533
534
535
536
537
538
539
		if(((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_ADD) &&
		   ((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)) {
			Debug(LDAP_DEBUG_ANY,
				"=> translucent_modify: silently dropped modification(%d): %s\n",
				m->sml_op, m->sml_desc->ad_cname.bv_val, 0);
			if((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) del++;
			continue;
		}
540
541
542
		atmp.a_desc = m->sml_desc;
		atmp.a_vals = m->sml_values;
		atmp.a_nvals = m->sml_nvalues ? m->sml_nvalues : atmp.a_vals;
543
544
		atmp.a_numvals = m->sml_numvals;
		atmp.a_flags = 0;
545
		a = attr_dup( &atmp );
546
547
548
549
		a->a_next  = ax;
		ax = a;
	}

Howard Chu's avatar
Howard Chu committed
550
	if(del && ov->strict) {
551
		attrs_free( a );
552
553
554
555
556
557
		send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
			"attempt to delete attributes from local database");
		return(rs->sr_err);
	}

	if(!ax) {
Howard Chu's avatar
Howard Chu committed
558
		if(ov->strict) {
559
560
561
562
563
564
565
566
567
568
			send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
				"modification contained other than ADD or REPLACE");
			return(rs->sr_err);
		}
		/* rs->sr_text = "no valid modification found"; */
		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result(op, rs);
		return(rs->sr_err);
	}

569
570
571
572
	e = entry_alloc();
	ber_dupbv( &e->e_name, &op->o_req_dn );
	ber_dupbv( &e->e_nname, &op->o_req_ndn );
	e->e_attrs = a;
573

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
574
	op->o_tag	= LDAP_REQ_ADD;
575
	cb.sc_response = translucent_tag_cb;
Howard Chu's avatar
Howard Chu committed
576
	cb.sc_private = op->orm_modlist;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
577
578
579
580
581
582
583
584
	op->oq_add.rs_e	= e;

	glue_parent(op);

	cb.sc_next = op->o_callback;
	op->o_callback = &cb;
	rc = on->on_info->oi_orig->bi_op_add(op, &nrs);
	if ( op->ora_e == e )
585
		entry_free( e );
586
	op->o_callback = cb.sc_next;
587
588
589
590
591
592

	return(rc);
}

static int translucent_compare(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
593
	translucent_info *ov = on->on_bi.bi_private;
594
	AttributeAssertion *ava = op->orc_ava;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
595
	Entry *e;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
596
	BackendDB *db;
597
598
599
600
601
602
603
604
605
606
	int rc;

	Debug(LDAP_DEBUG_TRACE, "==> translucent_compare: <%s> %s:%s\n",
		op->o_req_dn.bv_val, ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val);

/*
** if the local backend has an entry for this attribute:
**	CONTINUE and let it do the compare;
**
*/
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
607
	rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on);
608
	if(rc == LDAP_SUCCESS && e) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
609
		overlay_entry_release_ov(op, e, 0, on);
610
611
612
		return(SLAP_CB_CONTINUE);
	}

Howard Chu's avatar
Howard Chu committed
613
614
615
616
617
	if(ov->defer_db_open) {
		send_ldap_error(op, rs, LDAP_UNAVAILABLE,
			"remote DB not available");
		return(rs->sr_err);
	}
618
619
620
621
622
/*
** call compare() in the captive backend;
** return the result;
**
*/
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
623
624
625
626
	db = op->o_bd;
	op->o_bd = &ov->db;
	rc = ov->db.bd_info->bi_op_compare(op, rs);
	op->o_bd = db;
627
628
629
630
631
632

	return(rc);
}

/*
** translucent_search_cb()
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
633
**	merge local data with remote data
634
**
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
635
636
637
638
639
640
641
642
643
** Four cases:
** 1: remote search, no local filter
**	merge data and send immediately
** 2: remote search, with local filter
**	merge data and save
** 3: local search, no remote filter
**	merge data and send immediately
** 4: local search, with remote filter
**	check list, merge, send, delete
644
645
*/

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
646
647
648
649
650
651
652
653
654
655
656
657
#define	RMT_SIDE	0
#define	LCL_SIDE	1
#define	USE_LIST	2

typedef struct trans_ctx {
	BackendDB *db;
	slap_overinst *on;
	Filter *orig;
	Avlnode *list;
	int step;
} trans_ctx;

658
static int translucent_search_cb(Operation *op, SlapReply *rs) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
659
	trans_ctx *tc;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
660
	BackendDB *db;
661
	slap_overinst *on;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
662
663
	translucent_info *ov;
	Entry *le, *re;
664
	Attribute *a, *ax, *an, *as = NULL;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
665
	int rc;
666

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
667
668
669
670
671
672
	tc = op->o_callback->sc_private;

	/* Don't let the op complete while we're gathering data */
	if ( rs->sr_type == REP_RESULT && ( tc->step & USE_LIST ))
		return 0;

673
674
675
	if(!op || !rs || rs->sr_type != REP_SEARCH || !rs->sr_entry)
		return(SLAP_CB_CONTINUE);

Howard Chu's avatar
Howard Chu committed
676
	Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n",
677
678
		rs->sr_entry->e_name.bv_val, 0, 0);

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
679
680
	on = tc->on;
	ov = on->on_bi.bi_private;
681

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
	db = op->o_bd;
	re = NULL;

	/* If we have local, get remote */
	if ( tc->step & LCL_SIDE ) {
		le = rs->sr_entry;
		/* If entry is already on list, use it */
		if ( tc->step & USE_LIST ) {
			re = tavl_delete( &tc->list, le, entry_dn_cmp );
			if ( re ) {
				if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
					rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
					be_entry_release_r( op, rs->sr_entry );
				}
				if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
					rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
					entry_free( rs->sr_entry );
				}
				rc = test_filter( op, re, tc->orig );
				if ( rc == LDAP_COMPARE_TRUE ) {
					rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
					rs->sr_entry = re;
					return SLAP_CB_CONTINUE;
				} else {
					entry_free( re );
					rs->sr_entry = NULL;
					return 0;
				}
			}
		}
		op->o_bd = &ov->db;
		rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &re );
		if ( rc == LDAP_SUCCESS && re ) {
			Entry *tmp = entry_dup( re );
			be_entry_release_r( op, re );
			re = tmp;
		}
	} else {
	/* Else we have remote, get local */
		op->o_bd = tc->db;
		rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &le, on);
		if ( rc == LDAP_SUCCESS && le ) {
			re = entry_dup( rs->sr_entry );
			if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
				rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
				be_entry_release_r( op, rs->sr_entry );
			}
			if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
				rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
				entry_free( rs->sr_entry );
			}
		} else {
			le = NULL;
		}
	}
737
738

/*
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
739
** if we got remote and local entry:
740
**	foreach local attr:
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
741
742
**		foreach remote attr:
**			if match, remote attr with local attr;
743
**			if new local, add to list;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
744
**	append new local attrs to remote;
745
746
747
**
*/

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
748
749
	if ( re && le ) {
		for(ax = le->e_attrs; ax; ax = ax->a_next) {
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
			for(a = re->e_attrs; a; a = a->a_next) {
				if(a->a_desc == ax->a_desc) {
					if(a->a_vals != a->a_nvals)
						ber_bvarray_free(a->a_nvals);
					ber_bvarray_free(a->a_vals);
					a->a_vals = dup_bervarray(ax->a_vals);
					a->a_nvals = (ax->a_vals == ax->a_nvals) ?
						a->a_vals : dup_bervarray(ax->a_nvals);
					break;
				}
			}
			if(a) continue;
			an = attr_dup(ax);
			an->a_next = as;
			as = an;
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
766
767
768
769
770
771
772
773
774
775
776
777
778
		/* Dispose of local entry */
		if ( tc->step & LCL_SIDE ) {
			if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
				rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
				be_entry_release_r( op, rs->sr_entry );
			}
			if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
				rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
				entry_free( rs->sr_entry );
			}
		} else {
			overlay_entry_release_ov(op, le, 0, on);
		}
779
780
781
782
783
784
785
786
787
788

		/* literally append, so locals are always last */
		if(as) {
			if(re->e_attrs) {
				for(ax = re->e_attrs; ax->a_next; ax = ax->a_next);
				ax->a_next = as;
			} else {
				re->e_attrs = as;
			}
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
		/* If both filters, save entry for later */
		if ( tc->step == (USE_LIST|RMT_SIDE) ) {
			tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error );
			rs->sr_entry = NULL;
			rc = 0;
		} else {
		/* send it now */
			rs->sr_entry = re;
			rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
			rc = SLAP_CB_CONTINUE;
		}
	} else if ( le ) {
	/* Only a local entry: remote was deleted
	 * Ought to delete the local too...
	 */
	 	rc = 0;
	} else if ( tc->step & USE_LIST ) {
	/* Only a remote entry, but both filters:
	 * Test the complete filter
	 */
		rc = test_filter( op, rs->sr_entry, tc->orig );
		if ( rc == LDAP_COMPARE_TRUE ) {
			rc = SLAP_CB_CONTINUE;
		} else {
			rc = 0;
		}
	} else {
	/* Only a remote entry, only remote filter:
	 * just pass thru
	 */
		rc = SLAP_CB_CONTINUE;
820
821
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
822
	op->o_bd = db;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
	return rc;
}

/* Dup the filter, excluding invalid elements */
static Filter *
trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
{
	Filter *n = NULL;

	if ( !f )
		return NULL;

	switch( f->f_choice & SLAPD_FILTER_MASK ) {
	case SLAPD_FILTER_COMPUTED:
		n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
		n->f_choice = f->f_choice;
		n->f_result = f->f_result;
		n->f_next = NULL;
		break;

	case LDAP_FILTER_PRESENT:
		if ( ad_inlist( f->f_desc, an )) {
			n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
			n->f_choice = f->f_choice;
			n->f_desc = f->f_desc;
			n->f_next = NULL;
		}
		break;

	case LDAP_FILTER_EQUALITY:
	case LDAP_FILTER_GE:
	case LDAP_FILTER_LE:
	case LDAP_FILTER_APPROX:
	case LDAP_FILTER_SUBSTRINGS:
	case LDAP_FILTER_EXT:
		if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) {
			n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
			n->f_choice = f->f_choice;
			n->f_ava = f->f_ava;
			n->f_next = NULL;
		}
		break;

	case LDAP_FILTER_AND:
	case LDAP_FILTER_OR:
	case LDAP_FILTER_NOT: {
		Filter **p;

		n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
		n->f_choice = f->f_choice;
		n->f_next = NULL;

		for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
			*p = trans_filter_dup( op, f, an );
			if ( !*p )
				continue;
			p = &(*p)->f_next;
		}
		/* nothing valid in this list */
		if ( !n->f_list ) {
			op->o_tmpfree( n, op->o_tmpmemctx );
			return NULL;
		}
		/* Only 1 element in this list */
		if ((n->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
			!n->f_list->f_next ) {
			f = n->f_list;
			*n = *f;
			op->o_tmpfree( f, op->o_tmpmemctx );
		}
		break;
	}
	}
	return n;
}

static void
trans_filter_free( Operation *op, Filter *f )
{
	Filter *n, *p, *next;

	f->f_choice &= SLAPD_FILTER_MASK;

	switch( f->f_choice ) {
	case LDAP_FILTER_AND:
	case LDAP_FILTER_OR:
	case LDAP_FILTER_NOT:
		/* Free in reverse order */
		n = NULL;
		for ( p = f->f_list; p; p = next ) {
			next = p->f_next;
			p->f_next = n;
			n = p;
		}
		for ( p = n; p; p = next ) {
			next = p->f_next;
			trans_filter_free( op, p );
		}
		break;
	default:
		break;
	}
	op->o_tmpfree( f, op->o_tmpmemctx );
926
927
928
929
930
931
932
933
934
935
936
}

/*
** translucent_search()
**	search via captive backend;
**	override results with any local data;
**
*/

static int translucent_search(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
937
	translucent_info *ov = on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
938
	slap_callback cb = { NULL, NULL, NULL, NULL };
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
939
940
941
942
	trans_ctx tc;
	Filter *fl, *fr;
	struct berval fbv;
	int rc = 0;
943
944
945
946

	Debug(LDAP_DEBUG_TRACE, "==> translucent_search: <%s> %s\n",
		op->o_req_dn.bv_val, op->ors_filterstr.bv_val, 0);

Howard Chu's avatar
Howard Chu committed
947
948
949
950
951
	if(ov->defer_db_open) {
		send_ldap_error(op, rs, LDAP_UNAVAILABLE,
			"remote DB not available");
		return(rs->sr_err);
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
952
953
954

	fr = ov->remote ? trans_filter_dup( op, op->ors_filter, ov->remote ) : NULL;
	fl = ov->local ? trans_filter_dup( op, op->ors_filter, ov->local ) : NULL;
Howard Chu's avatar
Howard Chu committed
955
	cb.sc_response = (slap_response *) translucent_search_cb;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
956
	cb.sc_private = &tc;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
957
958
	cb.sc_next = op->o_callback;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
959
960
961
962
963
964
965
	tc.db = op->o_bd;
	tc.on = on;
	tc.orig = op->ors_filter;
	tc.list = NULL;
	tc.step = 0;
	fbv = op->ors_filterstr;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
966
	op->o_callback = &cb;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

	if ( fr || !fl ) {
		op->o_bd = &ov->db;
		tc.step |= RMT_SIDE;
		if ( fl ) {
			tc.step |= USE_LIST;
			op->ors_filter = fr;
			filter2bv_x( op, fr, &op->ors_filterstr );
		}
		rc = ov->db.bd_info->bi_op_search(op, rs);
		op->o_bd = tc.db;
		if ( fl ) {
			op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
		}
	}
	if ( fl && !rc ) {
		tc.step |= LCL_SIDE;
		op->ors_filter = fl;
		filter2bv_x( op, fl, &op->ors_filterstr );
		rc = overlay_op_walk( op, rs, op_search, on->on_info, on->on_next );
		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
	}
	op->ors_filterstr = fbv;
	op->ors_filter = tc.orig;
	op->o_callback = cb.sc_next;
	/* Send out anything remaining on the list and finish */
	if ( tc.step & USE_LIST ) {
		if ( tc.list ) {
			Avlnode *av;

			av = tavl_end( tc.list, TAVL_DIR_LEFT );
			while ( av ) {
				rs->sr_entry = av->avl_data;
				rc = test_filter( op, rs->sr_entry, op->ors_filter );
For faster browsing, not all history is shown. View entire blame