translucent.c 35 KB
Newer Older
1
2
3
4
/* translucent.c - translucent proxy module */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2004-2021 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 */
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;
43
	int defer_db_open;
44
	int bind_local;
45
	int pwmod_local;
Howard Chu's avatar
Howard Chu committed
46
} translucent_info;
47

Howard Chu's avatar
Howard Chu committed
48
49
50
static ConfigLDAPadd translucent_ldadd;
static ConfigCfAdd translucent_cfadd;

51
52
53
54
55
56
57
static ConfigDriver translucent_cf_gen;

enum {
	TRANS_LOCAL = 1,
	TRANS_REMOTE
};

Howard Chu's avatar
Howard Chu committed
58
59
60
61
62
63
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' "
64
	  "EQUALITY booleanMatch "
Howard Chu's avatar
Howard Chu committed
65
66
67
68
69
70
	  "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' "
71
	  "EQUALITY booleanMatch "
Howard Chu's avatar
Howard Chu committed
72
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
73
	{ "translucent_local", "attr[,attr...]", 1, 2, 0,
74
	  ARG_MAGIC|TRANS_LOCAL,
75
76
77
	  translucent_cf_gen,
	  "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' "
	  "DESC 'Attributes to use in local search filter' "
78
	  "EQUALITY caseIgnoreMatch "
79
80
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
	{ "translucent_remote", "attr[,attr...]", 1, 2, 0,
81
	  ARG_MAGIC|TRANS_REMOTE,
82
83
84
	  translucent_cf_gen,
	  "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
	  "DESC 'Attributes to use in remote search filter' "
85
	  "EQUALITY caseIgnoreMatch "
86
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
87
88
89
90
91
	{ "translucent_bind_local", "on|off", 1, 2, 0,
	  ARG_ON_OFF|ARG_OFFSET,
	  (void *)offsetof(translucent_info, bind_local),
	  "( OLcfgOvAt:14.5 NAME 'olcTranslucentBindLocal' "
	  "DESC 'Enable local bind' "
92
	  "EQUALITY booleanMatch "
93
	  "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
94
95
96
97
98
	{ "translucent_pwmod_local", "on|off", 1, 2, 0,
	  ARG_ON_OFF|ARG_OFFSET,
	  (void *)offsetof(translucent_info, pwmod_local),
	  "( OLcfgOvAt:14.6 NAME 'olcTranslucentPwModLocal' "
	  "DESC 'Enable local RFC 3062 Password Modify extended operation' "
99
	  "EQUALITY booleanMatch "
100
	  "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
101
102
103
104
105
106
107
108
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs translucentocs[] = {
	{ "( OLcfgOvOc:14.1 "
	  "NAME 'olcTranslucentConfig' "
	  "DESC 'Translucent configuration' "
	  "SUP olcOverlayConfig "
Howard Chu's avatar
Howard Chu committed
109
	  "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue $"
110
	  " olcTranslucentLocal $ olcTranslucentRemote $"
111
	  " olcTranslucentBindLocal $ olcTranslucentPwModLocal ) )",
Howard Chu's avatar
Howard Chu committed
112
113
114
115
	  Cft_Overlay, translucentcfg, NULL, translucent_cfadd },
	{ "( OLcfgOvOc:14.2 "
	  "NAME 'olcTranslucentDatabase' "
	  "DESC 'Translucent target database configuration' "
116
117
	/* co_table is initialized in translucent_initialize() */
	  "AUXILIARY )", Cft_Misc, NULL, translucent_ldadd },
Howard Chu's avatar
Howard Chu committed
118
119
	{ NULL, 0, NULL }
};
120
121
/* for translucent_init() */

122
123
124
static int
translucent_ldadd_cleanup( ConfigArgs *ca )
{
Howard Chu's avatar
Howard Chu committed
125
	slap_overinst *on = ca->ca_private;
126
127
128
129
130
131
	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
132
133
134
135
136
137
static int
translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
{
	slap_overinst *on;
	translucent_info *ov;

138
	Debug(LDAP_DEBUG_TRACE, "==> translucent_ldadd\n" );
Howard Chu's avatar
Howard Chu committed
139
140
141
142
143
144
145
146

	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;
Howard Chu's avatar
Howard Chu committed
147
	ca->ca_private = on;
148
	if ( CONFIG_ONLINE_ADD( ca ))
149
		config_push_cleanup( ca, translucent_ldadd_cleanup );
150
151
152
	else
		ov->defer_db_open = 0;

Howard Chu's avatar
Howard Chu committed
153
154
155
156
157
158
159
160
161
162
163
	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;

164
	Debug(LDAP_DEBUG_TRACE, "==> translucent_cfadd\n" );
Howard Chu's avatar
Howard Chu committed
165
166

	/* FIXME: should not hardcode "olcDatabase" here */
167
	bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
168
169
		"olcDatabase=" SLAP_X_ORDERED_FMT "%s",
		0, ov->db.bd_info->bi_type );
170
	if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
171
172
		return -1;
	}
173
	bv.bv_val = ca->cr_msg;
Howard Chu's avatar
Howard Chu committed
174
	ca->be = &ov->db;
175
	ov->defer_db_open = 0;
Howard Chu's avatar
Howard Chu committed
176
177
178
179
180
181
182
183
184
185
186

	/* 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;
}

187
188
189
190
191
192
193
194
195
196
197
198
199
200
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 ) {
Howard Chu's avatar
Howard Chu committed
201
202
		if ( !*an )
			return 1;
203
204
205
206
207
208
209
210
211
212
213
214
215
		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];
216
				i++;
217
218
219
220
221
222
223
224
225
			} 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,
226
			"%s: %s\n", c->log, c->cr_msg );
227
228
229
230
231
232
		return ARG_BAD_CONF;
	}
	*an = a2;
	return 0;
}

233
234
235
236
237
238
239
240
241
242
243
244
245
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
246
	struct berval ndn = BER_BVNULL;
247
248
	Attribute *a;
	Entry *e;
249
250
251
	struct berval	pdn;

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

254
	Debug(LDAP_DEBUG_TRACE, "=> glue_parent: fabricating glue for <%s>\n", ndn.bv_val );
255

256
	e = entry_alloc();
257
	e->e_id = NOID;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
258
259
	ber_dupbv(&e->e_name, &ndn);
	ber_dupbv(&e->e_nname, &ndn);
260

261
	a = attr_alloc( slap_schema.si_ad_objectClass );
262
	a->a_numvals = 2;
263
264
265
266
267
268
269
270
	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;

271
	a = attr_alloc( slap_schema.si_ad_structuralObjectClass );
272
	a->a_numvals = 1;
273
274
275
276
277
278
279
	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
280
281
	nop.o_req_dn = ndn;
	nop.o_req_ndn = ndn;
282
283
	nop.ora_e = e;

Howard Chu's avatar
Howard Chu committed
284
	nop.o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
285
	syncrepl_add_glue(&nop, e);
Howard Chu's avatar
Howard Chu committed
286
	nop.o_bd->bd_info = (BackendInfo *) on;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
287
288
289

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

290
291
292
293
294
295
296
297
	return;
}

/*
** 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
	Debug(LDAP_DEBUG_TRACE, "==> translucent_add: %s\n",
320
		op->o_req_dn.bv_val );
321
322
323
324
	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
	Debug(LDAP_DEBUG_TRACE, "==> translucent_modrdn: %s -> %s\n",
344
		op->o_req_dn.bv_val, op->orr_newrdn.bv_val );
345
346
347
348
	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);
	}
Howard Chu's avatar
Howard Chu 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
	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",
370
		op->o_req_dn.bv_val );
371
372
373
374
	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;
Howard Chu's avatar
Howard Chu committed
407
	BackendDB *db;
408
	int del, rc, erc = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
409
	slap_callback cb = { 0 };
410
411

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

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;
**
*/

Howard Chu's avatar
Howard Chu committed
426
427
	db = op->o_bd;
	op->o_bd = &ov->db;
428
	ov->db.be_acl = op->o_bd->be_acl;
Howard Chu's avatar
Howard Chu committed
429
	rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
Howard Chu's avatar
Howard Chu committed
430
	op->o_bd = db;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
431
	if(rc != LDAP_SUCCESS || re == NULL ) {
Howard Chu's avatar
Howard Chu committed
432
		send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
			"attempt to modify nonexistent local record");
		return(rs->sr_err);
	}
/*
** 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
449
	op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
450
	rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
Howard Chu's avatar
Howard Chu committed
451
	op->o_bd->bd_info = (BackendInfo *) on;
452
453

	if(e && rc == LDAP_SUCCESS) {
454
		Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: found local entry\n" );
Howard Chu's avatar
Howard Chu committed
455
456
		for(mm = &op->orm_modlist; *mm; ) {
			m = *mm;
457
458
			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
459
460
461
462
			if(a) {
				mm = &m->sml_next;
				continue;		/* found local attr */
			}
463
464
465
466
467
468
469
470
			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
471
				if(ov->strict) {
472
473
474
475
476
					erc = LDAP_CONSTRAINT_VIOLATION;
					goto release;
				}
				Debug(LDAP_DEBUG_TRACE,
					"=> translucent_modify: silently dropping delete: %s\n",
477
					m->sml_desc->ad_cname.bv_val );
Howard Chu's avatar
Howard Chu committed
478
				*mm = m->sml_next;
Howard Chu's avatar
Howard Chu committed
479
480
481
				m->sml_next = NULL;
				slap_mods_free(m, 1);
				continue;
482
483
			}
			m->sml_op = LDAP_MOD_ADD;
Howard Chu's avatar
Howard Chu committed
484
			mm = &m->sml_next;
485
486
487
488
		}
		erc = SLAP_CB_CONTINUE;
release:
		if(re) {
Howard Chu's avatar
Howard Chu committed
489
490
491
492
493
			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
494
495
				entry_free(re);
		}
Howard Chu's avatar
Howard Chu committed
496
		op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
497
		be_entry_release_r(op, e);
Howard Chu's avatar
Howard Chu committed
498
		op->o_bd->bd_info = (BackendInfo *) on;
499
500
501
502
503
504
505
506
507
		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
508
509
	/* don't leak remote entry copy */
	if(re) {
Howard Chu's avatar
Howard Chu committed
510
511
512
513
514
		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
515
516
			entry_free(re);
	}
517
518
519
520
/*
** foreach Modification:
**	if MOD_ADD or MOD_REPLACE, add Attribute;
** if no Modifications were suitable:
Howard Chu's avatar
Howard Chu committed
521
**	if strict, throw CONSTRAINT_VIOLATION;
522
523
524
525
526
527
528
**	else, return early SUCCESS;
** fabricate Entry with new Attribute chain;
** glue_parent() for this Entry;
** call bi_op_add() in local backend;
**
*/

529
	Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: fabricating local add\n" );
530
531
	a = NULL;
	for(del = 0, ax = NULL, m = op->orm_modlist; m; m = m->sml_next) {
532
		Attribute atmp;
533
534
535
536
		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",
537
				m->sml_op, m->sml_desc->ad_cname.bv_val );
538
539
540
			if((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) del++;
			continue;
		}
541
542
543
		atmp.a_desc = m->sml_desc;
		atmp.a_vals = m->sml_values;
		atmp.a_nvals = m->sml_nvalues ? m->sml_nvalues : atmp.a_vals;
544
545
		atmp.a_numvals = m->sml_numvals;
		atmp.a_flags = 0;
546
		a = attr_dup( &atmp );
547
548
549
550
		a->a_next  = ax;
		ax = a;
	}

Howard Chu's avatar
Howard Chu committed
551
	if(del && ov->strict) {
552
		attrs_free( a );
553
554
555
556
557
558
		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
559
		if(ov->strict) {
560
561
562
563
564
565
566
567
568
569
			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);
	}

570
571
572
573
	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;
574

Howard Chu's avatar
Howard Chu committed
575
	op->o_tag	= LDAP_REQ_ADD;
576
	cb.sc_response = translucent_tag_cb;
Howard Chu's avatar
Howard Chu committed
577
	cb.sc_private = op->orm_modlist;
Howard Chu's avatar
Howard Chu committed
578
579
580
581
582
583
584
585
	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 )
586
		entry_free( e );
Howard Chu's avatar
Howard Chu committed
587
	op->o_callback = cb.sc_next;
588
589
590
591
592
593

	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
594
	translucent_info *ov = on->on_bi.bi_private;
595
	AttributeAssertion *ava = op->orc_ava;
596
	Entry *e = NULL;
Howard Chu's avatar
Howard Chu committed
597
	BackendDB *db;
598
599
600
601
602
603
604
605
606
607
	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;
**
*/
Howard Chu's avatar
Howard Chu committed
608
	rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on);
609
	if(rc == LDAP_SUCCESS && e) {
Howard Chu's avatar
Howard Chu committed
610
		overlay_entry_release_ov(op, e, 0, on);
611
612
613
		return(SLAP_CB_CONTINUE);
	}

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

	return(rc);
}

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
static int translucent_pwmod(Operation *op, SlapReply *rs) {
	SlapReply nrs = { REP_RESULT };
	Operation nop;

	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	translucent_info *ov = on->on_bi.bi_private;
	Entry *e = NULL, *re = NULL;
	BackendDB *db;
	int rc = 0;
	slap_callback cb = { 0 };

	if (!ov->pwmod_local) {
		rs->sr_err = LDAP_CONSTRAINT_VIOLATION,
		rs->sr_text = "attempt to modify password in local database";
		return rs->sr_err;
	}

/*
** fetch entry from the captive backend;
** if it did not exist, fail;
** release it, if captive backend supports this;
**
*/
	db = op->o_bd;
	op->o_bd = &ov->db;
658
	ov->db.be_acl = op->o_bd->be_acl;
659
660
661
662
663
664
665
666
667
668
669
670
671
	rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
	if(rc != LDAP_SUCCESS || re == NULL ) {
		send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
			"attempt to modify nonexistent local record");
		return(rs->sr_err);
	}
	op->o_bd = db;
/*
** fetch entry from local backend;
** if it exists:
**	return CONTINUE;
*/

Howard Chu's avatar
Howard Chu committed
672
	op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
673
674
675
676
677
678
679
680
681
682
683
684
685
	rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
	op->o_bd->bd_info = (BackendInfo *) on;

	if(e && rc == LDAP_SUCCESS) {
		if(re) {
			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 {
				entry_free(re);
			}
		}
Howard Chu's avatar
Howard Chu committed
686
		op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
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
737
		be_entry_release_r(op, e);
		op->o_bd->bd_info = (BackendInfo *) on;
		return SLAP_CB_CONTINUE;
	}

	/* don't leak remote entry copy */
	if(re) {
		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 {
			entry_free(re);
		}
	}
/*
** glue_parent() for this Entry;
** call bi_op_add() in local backend;
**
*/
	e = entry_alloc();
	ber_dupbv( &e->e_name, &op->o_req_dn );
	ber_dupbv( &e->e_nname, &op->o_req_ndn );
	e->e_attrs = NULL;

	nop = *op;
	nop.o_tag = LDAP_REQ_ADD;
	cb.sc_response = slap_null_cb;
	nop.oq_add.rs_e	= e;

	glue_parent(&nop);

	nop.o_callback = &cb;
	rc = on->on_info->oi_orig->bi_op_add(&nop, &nrs);
	if ( nop.ora_e == e ) {
		entry_free( e );
	}

	if ( rc == LDAP_SUCCESS ) {
		return SLAP_CB_CONTINUE;
	}

	return rc;
}

static int translucent_exop(Operation *op, SlapReply *rs) {
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	translucent_info *ov = on->on_bi.bi_private;
	const struct berval bv_exop_pwmod = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);

	Debug(LDAP_DEBUG_TRACE, "==> translucent_exop: %s\n",
738
		op->o_req_dn.bv_val );
739
740
741
742
743
744
745
746
747
748
749
750
751
752

	if(ov->defer_db_open) {
		send_ldap_error(op, rs, LDAP_UNAVAILABLE,
			"remote DB not available");
		return(rs->sr_err);
	}

	if ( bvmatch( &bv_exop_pwmod, &op->ore_reqoid ) ) {
		return translucent_pwmod( op, rs );
	}

	return SLAP_CB_CONTINUE;
}

753
754
/*
** translucent_search_cb()
755
**	merge local data with remote data
756
**
757
758
759
760
761
762
763
764
765
** 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
766
767
*/

768
769
770
771
772
773
774
775
#define	RMT_SIDE	0
#define	LCL_SIDE	1
#define	USE_LIST	2

typedef struct trans_ctx {
	BackendDB *db;
	slap_overinst *on;
	Filter *orig;
776
	TAvlnode *list;
777
	int step;
778
	int slimit;
779
	AttributeName *attrs;
780
781
} trans_ctx;

782
static int translucent_search_cb(Operation *op, SlapReply *rs) {
783
	trans_ctx *tc;
784
	BackendDB *db;
785
	slap_overinst *on;
786
787
	translucent_info *ov;
	Entry *le, *re;
788
	Attribute *a, *ax, *an, *as = NULL;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
789
	int rc;
790
	int test_f = 0;
791

792
793
794
795
796
797
	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;

Howard Chu's avatar
Howard Chu committed
798
	if(rs->sr_type != REP_SEARCH || !rs->sr_entry)
799
800
		return(SLAP_CB_CONTINUE);

Howard Chu's avatar
Howard Chu committed
801
	Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n",
802
		rs->sr_entry->e_name.bv_val );
803

804
	op->ors_slimit = tc->slimit + ( tc->slimit > 0 ? 1 : 0 );
805
806
807
808
809
	if ( op->ors_attrs == slap_anlist_all_attributes ) {
		op->ors_attrs = tc->attrs;
		rs->sr_attrs = tc->attrs;
		rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
	}
810

811
812
	on = tc->on;
	ov = on->on_bi.bi_private;
813

814
815
816
817
818
819
820
821
822
823
	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 ) {
824
				rs_flush_entry( op, rs, on );
825
826
827
828
				rc = test_filter( op, re, tc->orig );
				if ( rc == LDAP_COMPARE_TRUE ) {
					rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
					rs->sr_entry = re;
829
830
831
832
833

					if ( tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
						return LDAP_SIZELIMIT_EXCEEDED;
					}

834
835
836
837
838
839
840
841
842
843
844
845
846
847
					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;
848
			test_f = 1;
849
850
851
852
		}
	} else {
	/* Else we have remote, get local */
		op->o_bd = tc->db;
853
		le = NULL;
854
855
856
		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 );
857
			rs_flush_entry( op, rs, on );
858
859
860
861
		} else {
			le = NULL;
		}
	}
862
863

/*
864
** if we got remote and local entry:
865
**	foreach local attr:
866
867
**		foreach remote attr:
**			if match, remote attr with local attr;
868
**			if new local, add to list;
869
**	append new local attrs to remote;
870
871
872
**
*/

873
874
	if ( re && le ) {
		for(ax = le->e_attrs; ax; ax = ax->a_next) {
875
876
			for(a = re->e_attrs; a; a = a->a_next) {
				if(a->a_desc == ax->a_desc) {
877
					test_f = 1;
878
879
880
					if(a->a_vals != a->a_nvals)
						ber_bvarray_free(a->a_nvals);
					ber_bvarray_free(a->a_vals);
881
882
883
884
885
886
					ber_bvarray_dup_x( &a->a_vals, ax->a_vals, NULL );
					if ( ax->a_vals == ax->a_nvals ) {
						a->a_nvals = a->a_vals;
					} else {
						ber_bvarray_dup_x( &a->a_nvals, ax->a_nvals, NULL );
					}
887
888
889
890
891
892
893
894
					break;
				}
			}
			if(a) continue;
			an = attr_dup(ax);
			an->a_next = as;
			as = an;
		}
895
896
		/* Dispose of local entry */
		if ( tc->step & LCL_SIDE ) {
897
			rs_flush_entry(op, rs, on);
898
899
900
		} else {
			overlay_entry_release_ov(op, le, 0, on);
		}
901
902
903
904
905
906
907
908
909
910

		/* 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;
			}
		}
911
		/* If both filters, save entry for later */
912
		if ( tc->step == (USE_LIST|RMT_SIDE) ) {
913
			tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error );
914
915
916
			rs->sr_entry = NULL;
			rc = 0;
		} else {
917
		/* send it now */
918
919
			rs->sr_entry = re;
			rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
920
921
922
923
924
925
926
927
928
929
			if ( test_f ) {
				rc = test_filter( op, rs->sr_entry, tc->orig );
				if ( rc == LDAP_COMPARE_TRUE ) {
					rc = SLAP_CB_CONTINUE;
				} else {
					rc = 0;
				}
			} else {
				rc = SLAP_CB_CONTINUE;
			}
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
		}
	} 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;
951
952
	}

953
	op->o_bd = db;
954
955
956
957
958

	if ( rc == SLAP_CB_CONTINUE && tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
		return LDAP_SIZELIMIT_EXCEEDED;
	}

959
960
961
962
963
964
965
966
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
	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;
For faster browsing, not all history is shown. View entire blame