1/*	$NetBSD: psearchctrl.c,v 1.2 2021/08/14 16:14:56 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 The OpenLDAP Foundation.
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 developed by Howard Chu for inclusion in
19 * OpenLDAP Software.
20 */
21
22#include <sys/cdefs.h>
23__RCSID("$NetBSD: psearchctrl.c,v 1.2 2021/08/14 16:14:56 christos Exp $");
24
25#include "portable.h"
26
27#include <stdio.h>
28#include <ac/stdlib.h>
29#include <ac/string.h>
30#include <ac/time.h>
31
32#include "ldap-int.h"
33
34/* Based on draft-ietf-ldapext-c-api-psearch-00 */
35
36/* ---------------------------------------------------------------------------
37   ldap_create_persistentsearch_control_value
38
39   Create and encode the value of the server-side sort control.
40
41   ld          (IN) An LDAP session handle, as obtained from a call to
42					ldap_init().
43
44   changetypes (IN) A bit-sensitive field that indicates which kinds of
45					changes the client wants to be informed about.  Its
46					value should be LDAP_CHANGETYPE_ANY, or any logical-OR
47					combination of LDAP_CHANGETYPE_ADD,
48					LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
49					LDAP_CHANGETYPE_MODDN.  This field corresponds to the
50					changeType element of the BER-encoded PersistentSearch
51					control value itself.
52
53   changesonly (IN) A Boolean field that indicates whether the client
54					wishes to only receive searchResultEntry messages for
55					entries that have been changed. If non-zero, only
56					entries that result from changes are returned; other-
57					wise, all of the static entries that match the search
58					criteria are returned before the server begins change
59					notification.  This field corresponds to the changes-
60					Only element of the BER-encoded PersistentSearch con-
61					trol value itself.
62
63   return_echg_ctls (IN) A Boolean field that indicates whether the server
64					should send back an Entry Change Notification control
65					with each searchResultEntry that is returned due to a
66					change to an entry.  If non-zero, Entry Change
67					Notification controls are requested; if zero, they are
68					not.  This field corresponds to the returnECs element
69					of the BER-encoded PersistentSearch control value
70					itself.
71
72   value      (OUT) Contains the control value; the bv_val member of the berval structure
73					SHOULD be freed by calling ldap_memfree() when done.
74
75   ---------------------------------------------------------------------------*/
76
77int
78ldap_create_persistentsearch_control_value(
79	LDAP *ld,
80	int changetypes,
81	int changesonly,
82	int return_echg_ctls,
83	struct berval *value )
84{
85	int		i;
86	BerElement	*ber = NULL;
87	ber_tag_t	tag;
88
89	assert( ld != NULL );
90	assert( LDAP_VALID( ld ) );
91
92	if ( ld == NULL ) return LDAP_PARAM_ERROR;
93	if ( value == NULL ) {
94		ld->ld_errno = LDAP_PARAM_ERROR;
95		return LDAP_PARAM_ERROR;
96	}
97	if (( changetypes & 0x0f ) != changetypes ) {
98		ld->ld_errno = LDAP_PARAM_ERROR;
99		return LDAP_PARAM_ERROR;
100	}
101
102	value->bv_val = NULL;
103	value->bv_len = 0;
104	ld->ld_errno = LDAP_SUCCESS;
105
106	ber = ldap_alloc_ber_with_options( ld );
107	if ( ber == NULL) {
108		ld->ld_errno = LDAP_NO_MEMORY;
109		return ld->ld_errno;
110	}
111
112	tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls );
113	if ( tag == LBER_ERROR ) {
114		goto error_return;
115	}
116
117	if ( ber_flatten2( ber, value, 1 ) == -1 ) {
118		ld->ld_errno = LDAP_NO_MEMORY;
119	}
120
121	if ( 0 ) {
122error_return:;
123		ld->ld_errno =  LDAP_ENCODING_ERROR;
124	}
125
126	if ( ber != NULL ) {
127		ber_free( ber, 1 );
128	}
129
130	return ld->ld_errno;
131}
132
133
134/* ---------------------------------------------------------------------------
135   ldap_create_persistentsearch_control
136
137   Create and encode the persistent search control.
138
139   ld          (IN) An LDAP session handle, as obtained from a call to
140					ldap_init().
141
142   changetypes (IN) A bit-sensitive field that indicates which kinds of
143					changes the client wants to be informed about.  Its
144					value should be LDAP_CHANGETYPE_ANY, or any logical-OR
145					combination of LDAP_CHANGETYPE_ADD,
146					LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
147					LDAP_CHANGETYPE_MODDN.  This field corresponds to the
148					changeType element of the BER-encoded PersistentSearch
149					control value itself.
150
151   changesonly (IN) A Boolean field that indicates whether the client
152					wishes to only receive searchResultEntry messages for
153					entries that have been changed. If non-zero, only
154					entries that result from changes are returned; other-
155					wise, all of the static entries that match the search
156					criteria are returned before the server begins change
157					notification.  This field corresponds to the changes-
158					Only element of the BER-encoded PersistentSearch con-
159					trol value itself.
160
161   return_echg_ctls (IN) A Boolean field that indicates whether the server
162					should send back an Entry Change Notification control
163					with each searchResultEntry that is returned due to a
164					change to an entry.  If non-zero, Entry Change
165					Notification controls are requested; if zero, they are
166					not.  This field corresponds to the returnECs element
167					of the BER-encoded PersistentSearch control value
168					itself.
169
170   isCritical  (IN) 0 - Indicates the control is not critical to the operation.
171					non-zero - The control is critical to the operation.
172
173   ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
174					SHOULD be freed by calling ldap_control_free() when done.
175
176   ---------------------------------------------------------------------------*/
177
178int
179ldap_create_persistentsearch_control(
180	LDAP *ld,
181	int changetypes,
182	int changesonly,
183	int return_echg_ctls,
184	int isCritical,
185	LDAPControl **ctrlp )
186{
187	struct berval	value;
188
189	assert( ld != NULL );
190	assert( LDAP_VALID( ld ) );
191
192	if ( ld == NULL ) {
193		return LDAP_PARAM_ERROR;
194	}
195
196	if ( ctrlp == NULL ) {
197		ld->ld_errno = LDAP_PARAM_ERROR;
198		return ld->ld_errno;
199	}
200
201	ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value );
202	if ( ld->ld_errno == LDAP_SUCCESS ) {
203		ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST,
204			isCritical, &value, 0, ctrlp );
205		if ( ld->ld_errno != LDAP_SUCCESS ) {
206			LDAP_FREE( value.bv_val );
207		}
208	}
209
210	return ld->ld_errno;
211}
212
213
214/* ---------------------------------------------------------------------------
215   ldap_parse_entrychange_control
216
217   Decode the entry change notification control return information.
218
219   ld          (IN) An LDAP session handle, as obtained from a call to
220					ldap_init().
221
222   ctrl        (IN) The address of the LDAP Control Structure.
223
224   chgtypep   (OUT) This result parameter is filled in with one of the
225					following values to indicate the type of change that was
226					made that caused the entry to be returned:
227					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1),
228					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2),
229					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or
230					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8).
231					If this parameter is NULL, the change type information
232					is not returned.
233
234   prevdnp    (OUT) This result parameter points to the DN the
235   					entry had before it was renamed and/or moved by a
236					modifyDN operation. It is set to NULL for other types
237					of changes. If this parameter is NULL, the previous DN
238					information is not returned. The returned value is a
239					pointer to the contents of the control; it is not a
240					copy of the data.
241
242   chgnumpresentp (OUT) This result parameter is filled in with a non-zero
243   					value if a change number was returned in the control
244					(the change number is optional and servers MAY choose
245					not to return it). If this parameter is NULL, no indication
246					of whether the change number was present is returned.
247
248   chgnump    (OUT) This result parameter is filled in with the change number
249   					if one was returned in the control. If this parameter
250					is NULL, the change number is not returned.
251
252   ---------------------------------------------------------------------------*/
253
254int
255ldap_parse_entrychange_control(
256	LDAP *ld,
257	LDAPControl *ctrl,
258	int *chgtypep,
259	struct berval *prevdnp,
260	int *chgnumpresentp,
261	long *chgnump )
262{
263	BerElement *ber;
264	ber_tag_t tag, berTag;
265	ber_len_t berLen;
266	ber_int_t chgtype;
267
268	assert( ld != NULL );
269	assert( LDAP_VALID( ld ) );
270	assert( ctrl != NULL );
271
272	if (ld == NULL) {
273		return LDAP_PARAM_ERROR;
274	}
275
276	if (ctrl == NULL) {
277		ld->ld_errno =  LDAP_PARAM_ERROR;
278		return(ld->ld_errno);
279	}
280
281	if ( !ctrl->ldctl_value.bv_val ) {
282		ld->ld_errno = LDAP_DECODING_ERROR;
283		return(ld->ld_errno);
284	}
285
286	/* Create a BerElement from the berval returned in the control. */
287	ber = ber_init(&ctrl->ldctl_value);
288
289	if (ber == NULL) {
290		ld->ld_errno = LDAP_NO_MEMORY;
291		return(ld->ld_errno);
292	}
293
294	if ( prevdnp != NULL ) {
295		BER_BVZERO( prevdnp );
296	}
297	if ( chgnumpresentp != NULL )
298		*chgnumpresentp = 0;
299	if ( chgnump != NULL )
300		*chgnump = 0;
301
302	/* Extract the change type from the control. */
303	tag = ber_scanf(ber, "{e" /*}*/, &chgtype);
304
305	if( tag != LBER_ENUMERATED ) {
306		ber_free(ber, 1);
307		ld->ld_errno = LDAP_DECODING_ERROR;
308		return(ld->ld_errno);
309	}
310	if ( chgtypep != NULL )
311		*chgtypep = chgtype;
312
313	tag = ber_peek_tag( ber, &berLen );
314	if ( berLen ) {
315		if (tag == LBER_OCTETSTRING) {
316			if (prevdnp != NULL) {
317				tag = ber_get_stringbv( ber, prevdnp, 0 );
318			} else {
319				struct berval bv;
320				tag = ber_skip_element( ber, &bv );
321			}
322			if ( tag == LBER_ERROR ) {
323				ber_free(ber, 1);
324				ld->ld_errno = LDAP_DECODING_ERROR;
325				return(ld->ld_errno);
326			}
327			tag = ber_peek_tag( ber, &berLen );
328		}
329
330		if ( chgnumpresentp != NULL || chgnump != NULL ) {
331			ber_int_t chgnum = 0;
332			int present = 0;
333			if (tag == LBER_INTEGER) {
334				present = 1;
335				tag = ber_get_int( ber, &chgnum );
336				if ( tag == LBER_ERROR ) {
337					ber_free(ber, 1);
338					ld->ld_errno = LDAP_DECODING_ERROR;
339					return(ld->ld_errno);
340				}
341				if ( chgnumpresentp != NULL )
342					*chgnumpresentp = present;
343				if ( chgnump != NULL )
344					*chgnump = chgnum;
345			}
346		}
347	}
348
349	ber_free(ber,1);
350
351	ld->ld_errno = LDAP_SUCCESS;
352	return(ld->ld_errno);
353}
354