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 
77 int
ldap_create_persistentsearch_control_value(LDAP * ld,int changetypes,int changesonly,int return_echg_ctls,struct berval * value)78 ldap_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 ) {
122 error_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 
178 int
ldap_create_persistentsearch_control(LDAP * ld,int changetypes,int changesonly,int return_echg_ctls,int isCritical,LDAPControl ** ctrlp)179 ldap_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 
254 int
ldap_parse_entrychange_control(LDAP * ld,LDAPControl * ctrl,int * chgtypep,struct berval * prevdnp,int * chgnumpresentp,long * chgnump)255 ldap_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