1/* cloak.c - Overlay to hide some attribute except if explicitely requested */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2008-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2008 Emmanuel Dreyfus
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* ACKNOWLEDGEMENTS:
18 * This work was originally developed by the Emmanuel Dreyfus for
19 * inclusion in OpenLDAP Software.
20 */
21
22#include "portable.h"
23
24#ifdef SLAPD_OVER_CLOAK
25
26#include <stdio.h>
27
28#include "ac/string.h"
29#include "ac/socket.h"
30
31#include "lutil.h"
32#include "slap.h"
33#include "config.h"
34
35enum { CLOAK_ATTR = 1 };
36
37typedef struct cloak_info_t {
38	ObjectClass 		*ci_oc;
39	AttributeDescription	*ci_ad;
40	struct cloak_info_t	*ci_next;
41} cloak_info_t;
42
43#define CLOAK_USAGE "\"cloak-attr <attr> [<class>]\": "
44
45static int
46cloak_cfgen( ConfigArgs *c )
47{
48	slap_overinst	*on = (slap_overinst *)c->bi;
49	cloak_info_t	*ci = (cloak_info_t *)on->on_bi.bi_private;
50
51	int		rc = 0, i;
52
53	if ( c->op == SLAP_CONFIG_EMIT ) {
54		switch( c->type ) {
55		case CLOAK_ATTR:
56			for ( i = 0; ci; i++, ci = ci->ci_next ) {
57				struct berval	bv;
58				int len;
59
60				assert( ci->ci_ad != NULL );
61
62				if ( ci->ci_oc != NULL )
63					len = snprintf( c->cr_msg,
64					sizeof( c->cr_msg ),
65					SLAP_X_ORDERED_FMT "%s %s", i,
66					ci->ci_ad->ad_cname.bv_val,
67					ci->ci_oc->soc_cname.bv_val );
68				else
69					len = snprintf( c->cr_msg,
70					sizeof( c->cr_msg ),
71					SLAP_X_ORDERED_FMT "%s", i,
72					ci->ci_ad->ad_cname.bv_val );
73
74				bv.bv_val = c->cr_msg;
75				bv.bv_len = len;
76				value_add_one( &c->rvalue_vals, &bv );
77			}
78			break;
79
80		default:
81			rc = 1;
82			break;
83		}
84
85		return rc;
86
87	} else if ( c->op == LDAP_MOD_DELETE ) {
88		cloak_info_t	*ci_next;
89
90		switch( c->type ) {
91		case CLOAK_ATTR:
92			for ( ci_next = ci, i = 0;
93			      ci_next, c->valx < 0 || i < c->valx;
94			      ci = ci_next, i++ ){
95
96				ci_next = ci->ci_next;
97
98				ch_free ( ci->ci_ad );
99				if ( ci->ci_oc != NULL )
100					ch_free ( ci->ci_oc );
101
102				ch_free( ci );
103			}
104			ci = (cloak_info_t *)on->on_bi.bi_private;
105			break;
106
107		default:
108			rc = 1;
109			break;
110		}
111
112		return rc;
113	}
114
115	switch( c->type ) {
116	case CLOAK_ATTR: {
117		ObjectClass		*oc = NULL;
118		AttributeDescription	*ad = NULL;
119		const char		*text;
120		cloak_info_t 	       **cip = NULL;
121		cloak_info_t 	        *ci_next = NULL;
122
123		if ( c->argc == 3 ) {
124			oc = oc_find( c->argv[ 2 ] );
125			if ( oc == NULL ) {
126				snprintf( c->cr_msg,
127					  sizeof( c->cr_msg ),
128					  CLOAK_USAGE
129					  "unable to find ObjectClass \"%s\"",
130					  c->argv[ 2 ] );
131				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
132				       c->log, c->cr_msg, 0 );
133				return 1;
134			}
135		}
136
137		rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
138		if ( rc != LDAP_SUCCESS ) {
139			snprintf( c->cr_msg, sizeof( c->cr_msg ), CLOAK_USAGE
140				"unable to find AttributeDescription \"%s\"",
141				c->argv[ 1 ] );
142			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
143				c->log, c->cr_msg, 0 );
144			return 1;
145		}
146
147		for ( i = 0, cip = (cloak_info_t **)&on->on_bi.bi_private;
148		      c->valx < 0 || i < c->valx, *cip;
149		      i++, cip = &(*cip)->ci_next ) {
150			if ( c->valx >= 0 && *cip == NULL ) {
151				snprintf( c->cr_msg, sizeof( c->cr_msg ),
152					CLOAK_USAGE
153					"invalid index {%d}\n",
154					c->valx );
155				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
156					c->log, c->cr_msg, 0 );
157				return 1;
158			}
159			ci_next = *cip;
160		}
161
162		*cip = (cloak_info_t *)SLAP_CALLOC( 1, sizeof( cloak_info_t ) );
163		(*cip)->ci_oc = oc;
164		(*cip)->ci_ad = ad;
165		(*cip)->ci_next = ci_next;
166
167		rc = 0;
168		break;
169	}
170
171	default:
172		rc = 1;
173		break;
174	}
175
176	return rc;
177}
178
179static int
180cloak_search_response_cb( Operation *op, SlapReply *rs )
181{
182	slap_callback   *sc;
183	cloak_info_t	*ci;
184	Entry		*e = NULL;
185	Entry		*me = NULL;
186
187	assert( op && op->o_callback && rs );
188
189	if ( rs->sr_type != REP_SEARCH || !rs->sr_entry ) {
190		return ( SLAP_CB_CONTINUE );
191	}
192
193	sc = op->o_callback;
194	e = rs->sr_entry;
195
196	/*
197	 * First perform a quick scan for an attribute to cloak
198	 */
199	for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
200		Attribute *a;
201
202		if ( ci->ci_oc != NULL &&
203		     !is_entry_objectclass_or_sub( e, ci->ci_oc ) )
204			continue;
205
206		for ( a = e->e_attrs; a; a = a->a_next )
207			if ( a->a_desc == ci->ci_ad )
208				break;
209
210		if ( a != NULL )
211			break;
212	}
213
214	/*
215	 * Nothing found to cloak
216	 */
217	if ( ci == NULL )
218		return ( SLAP_CB_CONTINUE );
219
220	/*
221	 * We are now committed to cloak an attribute.
222	 */
223	rs_entry2modifiable( op, rs, (slap_overinst *) op->o_bd->bd_info );
224	me = rs->sr_entry;
225
226	for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
227		Attribute *a;
228		Attribute *pa;
229
230		for ( pa = NULL, a = me->e_attrs;
231		      a;
232		      pa = a, a = a->a_next ) {
233
234			if ( a->a_desc != ci->ci_ad )
235				continue;
236
237			Debug( LDAP_DEBUG_TRACE, "cloak_search_response_cb: cloak %s\n",
238			       a->a_desc->ad_cname.bv_val,
239			       0, 0 );
240
241			if ( pa != NULL )
242				pa->a_next = a->a_next;
243			else
244				me->e_attrs = a->a_next;
245
246			attr_clean( a );
247		}
248
249	}
250
251	return ( SLAP_CB_CONTINUE );
252}
253
254static int
255cloak_search_cleanup_cb( Operation *op, SlapReply *rs )
256{
257	if ( rs->sr_type == REP_RESULT || rs->sr_err != LDAP_SUCCESS ) {
258		slap_freeself_cb( op, rs );
259	}
260
261	return SLAP_CB_CONTINUE;
262}
263
264static int
265cloak_search( Operation *op, SlapReply *rs )
266{
267	slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
268	cloak_info_t    *ci = (cloak_info_t *)on->on_bi.bi_private;
269	slap_callback	*sc;
270
271	if ( op->ors_attrsonly ||
272	     op->ors_attrs ||
273	     get_manageDSAit( op ) )
274		return SLAP_CB_CONTINUE;
275
276	sc = op->o_tmpcalloc( 1, sizeof( *sc ), op->o_tmpmemctx );
277	sc->sc_response = cloak_search_response_cb;
278	sc->sc_cleanup = cloak_search_cleanup_cb;
279	sc->sc_next = op->o_callback;
280	sc->sc_private = ci;
281	op->o_callback = sc;
282
283	return SLAP_CB_CONTINUE;
284}
285
286static slap_overinst cloak_ovl;
287
288static ConfigTable cloakcfg[] = {
289	{ "cloak-attr", "attribute [class]",
290		2, 3, 0, ARG_MAGIC|CLOAK_ATTR, cloak_cfgen,
291		"( OLcfgCtAt:4.1 NAME 'olcCloakAttribute' "
292			"DESC 'Cloaked attribute: attribute [class]' "
293			"EQUALITY caseIgnoreMatch "
294			"SYNTAX OMsDirectoryString "
295			"X-ORDERED 'VALUES' )",
296			NULL, NULL },
297	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
298};
299
300static int
301cloak_db_destroy(
302	BackendDB *be,
303	ConfigReply *cr )
304{
305	slap_overinst *on = (slap_overinst *)be->bd_info;
306	cloak_info_t	*ci = (cloak_info_t *)on->on_bi.bi_private;
307
308	for ( ; ci; ) {
309		cloak_info_t *tmp = ci;
310		ci = ci->ci_next;
311		SLAP_FREE( tmp );
312	}
313
314	on->on_bi.bi_private = NULL;
315
316	return 0;
317}
318
319static ConfigOCs cloakocs[] = {
320	{ "( OLcfgCtOc:4.1 "
321	  "NAME 'olcCloakConfig' "
322	  "DESC 'Attribute cloak configuration' "
323	  "SUP olcOverlayConfig "
324	  "MAY ( olcCloakAttribute ) )",
325	  Cft_Overlay, cloakcfg },
326	{ NULL, 0, NULL }
327};
328
329#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
330static
331#endif
332int
333cloak_initialize( void ) {
334	int rc;
335	cloak_ovl.on_bi.bi_type = "cloak";
336	cloak_ovl.on_bi.bi_db_destroy = cloak_db_destroy;
337	cloak_ovl.on_bi.bi_op_search = cloak_search;
338        cloak_ovl.on_bi.bi_cf_ocs = cloakocs;
339
340	rc = config_register_schema ( cloakcfg, cloakocs );
341	if ( rc )
342		return rc;
343
344	return overlay_register( &cloak_ovl );
345}
346
347#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
348int init_module(int argc, char *argv[]) {
349	return cloak_initialize();
350}
351#endif
352
353#endif /* defined(SLAPD_OVER_CLOAK) */
354
355