1/*	$NetBSD: dyngroup.c,v 1.3 2021/08/14 16:15:02 christos Exp $	*/
2
3/* dyngroup.c - Demonstration of overlay code */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2003-2021 The OpenLDAP Foundation.
8 * Copyright 2003 by Howard Chu.
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/* ACKNOWLEDGEMENTS:
20 * This work was initially developed by Howard Chu for inclusion in
21 * OpenLDAP Software.
22 */
23
24#include <sys/cdefs.h>
25__RCSID("$NetBSD: dyngroup.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
26
27#include "portable.h"
28
29#ifdef SLAPD_OVER_DYNGROUP
30
31#include <stdio.h>
32
33#include <ac/string.h>
34#include <ac/socket.h>
35
36#include "lutil.h"
37#include "slap.h"
38#include "slap-config.h"
39
40/* This overlay extends the Compare operation to detect members of a
41 * dynamic group. It has no effect on any other operations. It must
42 * be configured with a pair of attributes to trigger on, e.g.
43 *	attrpair member memberURL
44 * will cause compares on "member" to trigger a compare on "memberURL".
45 */
46
47typedef struct adpair {
48	struct adpair *ap_next;
49	AttributeDescription *ap_mem;
50	AttributeDescription *ap_uri;
51} adpair;
52
53static int dgroup_cf( ConfigArgs *c )
54{
55	slap_overinst *on = (slap_overinst *)c->bi;
56	int rc = 1;
57
58	switch( c->op ) {
59	case SLAP_CONFIG_EMIT:
60		{
61		adpair *ap;
62		for ( ap = on->on_bi.bi_private; ap; ap = ap->ap_next ) {
63			struct berval bv;
64			char *ptr;
65			bv.bv_len = ap->ap_mem->ad_cname.bv_len + 1 +
66				ap->ap_uri->ad_cname.bv_len;
67			bv.bv_val = ch_malloc( bv.bv_len + 1 );
68			ptr = lutil_strcopy( bv.bv_val, ap->ap_mem->ad_cname.bv_val );
69			*ptr++ = ' ';
70			strcpy( ptr, ap->ap_uri->ad_cname.bv_val );
71			ber_bvarray_add( &c->rvalue_vals, &bv );
72			rc = 0;
73		}
74		}
75		break;
76	case LDAP_MOD_DELETE:
77		if ( c->valx == -1 ) {
78			adpair *ap;
79			while (( ap = on->on_bi.bi_private )) {
80				on->on_bi.bi_private = ap->ap_next;
81				ch_free( ap );
82			}
83		} else {
84			adpair **app, *ap;
85			int i;
86			app = (adpair **)&on->on_bi.bi_private;
87			for (i=0; i<=c->valx; i++, app = &ap->ap_next) {
88				ap = *app;
89			}
90			*app = ap->ap_next;
91			ch_free( ap );
92		}
93		rc = 0;
94		break;
95	case SLAP_CONFIG_ADD:
96	case LDAP_MOD_ADD:
97		{
98		adpair ap = { NULL, NULL, NULL }, *a2;
99		const char *text;
100		if ( slap_str2ad( c->argv[1], &ap.ap_mem, &text ) ) {
101			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s attribute description unknown: \"%s\"",
102				c->argv[0], c->argv[1] );
103			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
104				"%s: %s\n", c->log, c->cr_msg );
105			return ARG_BAD_CONF;
106		}
107		if ( slap_str2ad( c->argv[2], &ap.ap_uri, &text ) ) {
108			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s attribute description unknown: \"%s\"",
109				c->argv[0], c->argv[2] );
110			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
111				"%s: %s\n", c->log, c->cr_msg );
112			return ARG_BAD_CONF;
113		}
114		/* The on->on_bi.bi_private pointer can be used for
115		 * anything this instance of the overlay needs.
116		 */
117		a2 = ch_malloc( sizeof(adpair) );
118		a2->ap_next = on->on_bi.bi_private;
119		a2->ap_mem = ap.ap_mem;
120		a2->ap_uri = ap.ap_uri;
121		on->on_bi.bi_private = a2;
122		rc = 0;
123		}
124	}
125	return rc;
126}
127
128static ConfigTable dgroupcfg[] = {
129	{ "attrpair", "member-attribute> <URL-attribute", 3, 3, 0,
130	  ARG_MAGIC, dgroup_cf,
131	  "( OLcfgOvAt:17.1 NAME ( 'olcDynGroupAttrPair' 'olcDGAttrPair' ) "
132	  "EQUALITY caseIgnoreMatch "
133	  "DESC 'Member and MemberURL attribute pair' "
134	  "SYNTAX OMsDirectoryString )", NULL, NULL },
135	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
136};
137
138static ConfigOCs dgroupocs[] = {
139	{ "( OLcfgOvOc:17.1 "
140	  "NAME ( 'olcDynGroupConfig' 'olcDGConfig' ) "
141	  "DESC 'Dynamic Group configuration' "
142	  "SUP olcOverlayConfig "
143	  "MAY olcDynGroupAttrPair)",
144	  Cft_Overlay, dgroupcfg },
145	{ NULL, 0, NULL }
146};
147
148static int
149dyngroup_response( Operation *op, SlapReply *rs )
150{
151	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
152	adpair *ap = on->on_bi.bi_private;
153
154	/* If we've been configured and the current response is
155	 * what we're looking for...
156	 */
157	if ( ap && op->o_tag == LDAP_REQ_COMPARE &&
158		rs->sr_err == LDAP_NO_SUCH_ATTRIBUTE ) {
159
160		for (;ap;ap=ap->ap_next) {
161			if ( op->oq_compare.rs_ava->aa_desc == ap->ap_mem ) {
162				/* This compare is for one of the attributes we're
163				 * interested in. We'll use slapd's existing dyngroup
164				 * evaluator to get the answer we want.
165				 */
166				int cache = op->o_do_not_cache;
167
168				op->o_do_not_cache = 1;
169				rs->sr_err = backend_group( op, NULL, &op->o_req_ndn,
170					&op->oq_compare.rs_ava->aa_value, NULL, ap->ap_uri );
171				op->o_do_not_cache = cache;
172				switch ( rs->sr_err ) {
173				case LDAP_SUCCESS:
174					rs->sr_err = LDAP_COMPARE_TRUE;
175					break;
176
177				case LDAP_NO_SUCH_OBJECT:
178					rs->sr_err = LDAP_COMPARE_FALSE;
179					break;
180				}
181				break;
182			}
183		}
184	}
185	/* Default is to just fall through to the normal processing */
186	return SLAP_CB_CONTINUE;
187}
188
189static int
190dyngroup_destroy(
191	BackendDB *be,
192	ConfigReply *cr
193)
194{
195	slap_overinst *on = (slap_overinst *) be->bd_info;
196	adpair *ap, *a2;
197
198	for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
199		a2 = ap->ap_next;
200		ch_free( ap );
201	}
202	return 0;
203}
204
205static slap_overinst dyngroup;
206
207/* This overlay is set up for dynamic loading via moduleload. For static
208 * configuration, you'll need to arrange for the slap_overinst to be
209 * initialized and registered by some other function inside slapd.
210 */
211
212int dyngroup_initialize() {
213	int code;
214
215	dyngroup.on_bi.bi_type = "dyngroup";
216	dyngroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
217	dyngroup.on_bi.bi_db_destroy = dyngroup_destroy;
218	dyngroup.on_response = dyngroup_response;
219
220	dyngroup.on_bi.bi_cf_ocs = dgroupocs;
221	code = config_register_schema( dgroupcfg, dgroupocs );
222	if ( code ) return code;
223
224	return overlay_register( &dyngroup );
225}
226
227#if SLAPD_OVER_DYNGROUP == SLAPD_MOD_DYNAMIC
228int
229init_module( int argc, char *argv[] )
230{
231	return dyngroup_initialize();
232}
233#endif
234
235#endif /* defined(SLAPD_OVER_DYNGROUP) */
236