noopsrch.c revision 1.4 1 1.3 christos /* $NetBSD: noopsrch.c,v 1.4 2025/09/05 21:16:17 christos Exp $ */
2 1.1 tron
3 1.1 tron /* noopsrch.c - LDAP Control that counts entries a search would return */
4 1.1 tron /* $OpenLDAP$ */
5 1.1 tron /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 1.1 tron *
7 1.4 christos * Copyright 2010-2024 The OpenLDAP Foundation.
8 1.1 tron * All rights reserved.
9 1.1 tron *
10 1.1 tron * Redistribution and use in source and binary forms, with or without
11 1.1 tron * modification, are permitted only as authorized by the OpenLDAP
12 1.1 tron * Public License.
13 1.1 tron *
14 1.1 tron * A copy of this license is available in the file LICENSE in the
15 1.1 tron * top-level directory of the distribution or, alternatively, at
16 1.1 tron * <http://www.OpenLDAP.org/license.html>.
17 1.1 tron */
18 1.1 tron /* ACKNOWLEDGEMENTS:
19 1.1 tron * This work was initially developed by Pierangelo Masarati for inclusion
20 1.1 tron * in OpenLDAP Software.
21 1.1 tron */
22 1.1 tron
23 1.2 christos #include <sys/cdefs.h>
24 1.3 christos __RCSID("$NetBSD: noopsrch.c,v 1.4 2025/09/05 21:16:17 christos Exp $");
25 1.2 christos
26 1.1 tron #include "portable.h"
27 1.1 tron
28 1.1 tron /* define SLAPD_OVER_NOOPSRCH=2 to build as run-time loadable module */
29 1.1 tron #ifdef SLAPD_OVER_NOOPSRCH
30 1.1 tron
31 1.1 tron /*
32 1.1 tron * Control OID
33 1.1 tron */
34 1.1 tron #define LDAP_CONTROL_X_NOOPSRCH "1.3.6.1.4.1.4203.666.5.18"
35 1.1 tron
36 1.1 tron #include "slap.h"
37 1.1 tron #include "ac/string.h"
38 1.1 tron
39 1.1 tron #define o_noopsrch o_ctrlflag[noopsrch_cid]
40 1.1 tron #define o_ctrlnoopsrch o_controls[noopsrch_cid]
41 1.1 tron
42 1.1 tron static int noopsrch_cid;
43 1.1 tron static slap_overinst noopsrch;
44 1.1 tron
45 1.1 tron static int
46 1.1 tron noopsrch_parseCtrl (
47 1.1 tron Operation *op,
48 1.1 tron SlapReply *rs,
49 1.1 tron LDAPControl *ctrl )
50 1.1 tron {
51 1.1 tron if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
52 1.1 tron rs->sr_text = "No-op Search control specified multiple times";
53 1.1 tron return LDAP_PROTOCOL_ERROR;
54 1.1 tron }
55 1.1 tron
56 1.1 tron if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
57 1.1 tron rs->sr_text = "No-op Search control value is present";
58 1.1 tron return LDAP_PROTOCOL_ERROR;
59 1.1 tron }
60 1.1 tron
61 1.1 tron op->o_ctrlnoopsrch = (void *)NULL;
62 1.1 tron
63 1.1 tron op->o_noopsrch = ctrl->ldctl_iscritical
64 1.1 tron ? SLAP_CONTROL_CRITICAL
65 1.1 tron : SLAP_CONTROL_NONCRITICAL;
66 1.1 tron
67 1.1 tron rs->sr_err = LDAP_SUCCESS;
68 1.1 tron
69 1.1 tron return rs->sr_err;
70 1.1 tron }
71 1.1 tron
72 1.1 tron int dummy;
73 1.1 tron
74 1.1 tron typedef struct noopsrch_cb_t {
75 1.1 tron slap_overinst *nc_on;
76 1.1 tron ber_int_t nc_nentries;
77 1.1 tron ber_int_t nc_nsearchref;
78 1.1 tron AttributeName *nc_save_attrs;
79 1.1 tron int *nc_pdummy;
80 1.1 tron int nc_save_slimit;
81 1.1 tron } noopsrch_cb_t;
82 1.1 tron
83 1.1 tron static int
84 1.1 tron noopsrch_response( Operation *op, SlapReply *rs )
85 1.1 tron {
86 1.1 tron noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
87 1.1 tron
88 1.1 tron /* if the control is global, limits are not computed yet */
89 1.1 tron if ( nc->nc_pdummy == &dummy ) {
90 1.1 tron nc->nc_save_slimit = op->ors_slimit;
91 1.1 tron op->ors_slimit = SLAP_NO_LIMIT;
92 1.1 tron nc->nc_pdummy = NULL;
93 1.1 tron }
94 1.1 tron
95 1.1 tron if ( rs->sr_type == REP_SEARCH ) {
96 1.1 tron nc->nc_nentries++;
97 1.1 tron #ifdef NOOPSRCH_DEBUG
98 1.3 christos Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_SEARCH): nentries=%d\n", nc->nc_nentries );
99 1.1 tron #endif
100 1.1 tron return 0;
101 1.1 tron
102 1.1 tron } else if ( rs->sr_type == REP_SEARCHREF ) {
103 1.1 tron nc->nc_nsearchref++;
104 1.1 tron return 0;
105 1.1 tron
106 1.1 tron } else if ( rs->sr_type == REP_RESULT ) {
107 1.1 tron BerElementBuffer berbuf;
108 1.1 tron BerElement *ber = (BerElement *) &berbuf;
109 1.1 tron struct berval ctrlval;
110 1.1 tron LDAPControl *ctrl, *ctrlsp[2];
111 1.1 tron int rc = rs->sr_err;
112 1.1 tron
113 1.1 tron if ( nc->nc_save_slimit >= 0 && nc->nc_nentries >= nc->nc_save_slimit ) {
114 1.1 tron rc = LDAP_SIZELIMIT_EXCEEDED;
115 1.1 tron }
116 1.1 tron
117 1.1 tron #ifdef NOOPSRCH_DEBUG
118 1.1 tron Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_RESULT): err=%d nentries=%d nref=%d\n", rc, nc->nc_nentries, nc->nc_nsearchref );
119 1.1 tron #endif
120 1.1 tron
121 1.1 tron ber_init2( ber, NULL, LBER_USE_DER );
122 1.1 tron
123 1.1 tron ber_printf( ber, "{iii}", rc, nc->nc_nentries, nc->nc_nsearchref );
124 1.1 tron if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
125 1.1 tron ber_free_buf( ber );
126 1.1 tron if ( op->o_noopsrch == SLAP_CONTROL_CRITICAL ) {
127 1.1 tron return LDAP_CONSTRAINT_VIOLATION;
128 1.1 tron }
129 1.1 tron return SLAP_CB_CONTINUE;
130 1.1 tron }
131 1.1 tron
132 1.1 tron ctrl = op->o_tmpcalloc( 1,
133 1.1 tron sizeof( LDAPControl ) + ctrlval.bv_len + 1,
134 1.1 tron op->o_tmpmemctx );
135 1.1 tron ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
136 1.1 tron ctrl->ldctl_oid = LDAP_CONTROL_X_NOOPSRCH;
137 1.1 tron ctrl->ldctl_iscritical = 0;
138 1.1 tron ctrl->ldctl_value.bv_len = ctrlval.bv_len;
139 1.1 tron AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
140 1.1 tron ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
141 1.1 tron
142 1.1 tron ber_free_buf( ber );
143 1.1 tron
144 1.1 tron ctrlsp[0] = ctrl;
145 1.1 tron ctrlsp[1] = NULL;
146 1.1 tron slap_add_ctrls( op, rs, ctrlsp );
147 1.1 tron }
148 1.2 christos return SLAP_CB_CONTINUE;
149 1.1 tron }
150 1.1 tron
151 1.1 tron static int
152 1.1 tron noopsrch_cleanup( Operation *op, SlapReply *rs )
153 1.1 tron {
154 1.1 tron if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
155 1.1 tron noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
156 1.1 tron op->ors_attrs = nc->nc_save_attrs;
157 1.1 tron if ( nc->nc_pdummy == NULL ) {
158 1.1 tron op->ors_slimit = nc->nc_save_slimit;
159 1.1 tron }
160 1.1 tron
161 1.1 tron op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
162 1.1 tron op->o_callback = NULL;
163 1.1 tron }
164 1.1 tron
165 1.1 tron return SLAP_CB_CONTINUE;
166 1.1 tron }
167 1.1 tron
168 1.1 tron static int
169 1.1 tron noopsrch_op_search( Operation *op, SlapReply *rs )
170 1.1 tron {
171 1.1 tron if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
172 1.1 tron slap_callback *sc;
173 1.1 tron noopsrch_cb_t *nc;
174 1.1 tron
175 1.1 tron sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( noopsrch_cb_t ), op->o_tmpmemctx );
176 1.1 tron
177 1.1 tron nc = (noopsrch_cb_t *)&sc[ 1 ];
178 1.1 tron nc->nc_on = (slap_overinst *)op->o_bd->bd_info;
179 1.1 tron nc->nc_nentries = 0;
180 1.1 tron nc->nc_nsearchref = 0;
181 1.1 tron nc->nc_save_attrs = op->ors_attrs;
182 1.1 tron nc->nc_pdummy = &dummy;
183 1.1 tron
184 1.1 tron sc->sc_response = noopsrch_response;
185 1.1 tron sc->sc_cleanup = noopsrch_cleanup;
186 1.1 tron sc->sc_private = (void *)nc;
187 1.1 tron
188 1.1 tron op->ors_attrs = slap_anlist_no_attrs;
189 1.1 tron
190 1.1 tron sc->sc_next = op->o_callback->sc_next;
191 1.1 tron op->o_callback->sc_next = sc;
192 1.1 tron }
193 1.1 tron
194 1.1 tron return SLAP_CB_CONTINUE;
195 1.1 tron }
196 1.1 tron
197 1.1 tron static int noopsrch_cnt;
198 1.1 tron
199 1.1 tron static int
200 1.1 tron noopsrch_db_init( BackendDB *be, ConfigReply *cr)
201 1.1 tron {
202 1.1 tron if ( noopsrch_cnt++ == 0 ) {
203 1.1 tron int rc;
204 1.1 tron
205 1.1 tron rc = register_supported_control( LDAP_CONTROL_X_NOOPSRCH,
206 1.1 tron SLAP_CTRL_SEARCH | SLAP_CTRL_GLOBAL_SEARCH, NULL,
207 1.1 tron noopsrch_parseCtrl, &noopsrch_cid );
208 1.1 tron if ( rc != LDAP_SUCCESS ) {
209 1.1 tron Debug( LDAP_DEBUG_ANY,
210 1.1 tron "noopsrch_initialize: Failed to register control '%s' (%d)\n",
211 1.3 christos LDAP_CONTROL_X_NOOPSRCH, rc );
212 1.1 tron return rc;
213 1.1 tron }
214 1.1 tron }
215 1.1 tron
216 1.1 tron return LDAP_SUCCESS;
217 1.1 tron }
218 1.1 tron
219 1.1 tron static int
220 1.1 tron noopsrch_db_destroy( BackendDB *be, ConfigReply *cr )
221 1.1 tron {
222 1.1 tron assert( noopsrch_cnt > 0 );
223 1.1 tron
224 1.1 tron #ifdef SLAP_CONFIG_DELETE
225 1.1 tron overlay_unregister_control( be, LDAP_CONTROL_X_NOOPSRCH );
226 1.1 tron if ( --noopsrch_cnt == 0 ) {
227 1.1 tron unregister_supported_control( LDAP_CONTROL_X_NOOPSRCH );
228 1.1 tron }
229 1.1 tron
230 1.1 tron #endif /* SLAP_CONFIG_DELETE */
231 1.1 tron
232 1.1 tron return 0;
233 1.1 tron }
234 1.1 tron
235 1.1 tron #if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
236 1.1 tron static
237 1.1 tron #endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
238 1.1 tron int
239 1.1 tron noopsrch_initialize( void )
240 1.1 tron {
241 1.1 tron
242 1.1 tron noopsrch.on_bi.bi_type = "noopsrch";
243 1.1 tron
244 1.3 christos noopsrch.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
245 1.1 tron noopsrch.on_bi.bi_db_init = noopsrch_db_init;
246 1.1 tron noopsrch.on_bi.bi_db_destroy = noopsrch_db_destroy;
247 1.1 tron noopsrch.on_bi.bi_op_search = noopsrch_op_search;
248 1.1 tron
249 1.1 tron return overlay_register( &noopsrch );
250 1.1 tron }
251 1.1 tron
252 1.1 tron #if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
253 1.1 tron int
254 1.1 tron init_module( int argc, char *argv[] )
255 1.1 tron {
256 1.1 tron return noopsrch_initialize();
257 1.1 tron }
258 1.1 tron #endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
259 1.1 tron
260 1.1 tron #endif /* SLAPD_OVER_NOOPSRCH */
261