1 /*        $NetBSD: sortctrl.c,v 1.3 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 /* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
18  *
19  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
20  * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
21  * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
22  * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
23  * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
24  * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
25  * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
26  * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
27  */
28 /* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
29  * can be found in the file "build/LICENSE-2.0.1" in this distribution
30  * of OpenLDAP Software.
31  */
32 
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: sortctrl.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
35 
36 #include "portable.h"
37 
38 #include <stdio.h>
39 #include <ac/stdlib.h>
40 #include <ac/string.h>
41 #include <ac/time.h>
42 
43 #include "ldap-int.h"
44 
45 #define LDAP_MATCHRULE_IDENTIFIER      0x80L
46 #define LDAP_REVERSEORDER_IDENTIFIER   0x81L
47 #define LDAP_ATTRTYPES_IDENTIFIER      0x80L
48 
49 
50 
51 /* ---------------------------------------------------------------------------
52    countKeys
53 
54    Internal function to determine the number of keys in the string.
55 
56    keyString  (IN) String of items separated by whitespace.
57    ---------------------------------------------------------------------------*/
58 
countKeys(char * keyString)59 static int countKeys(char *keyString)
60 {
61           char *p = keyString;
62           int count = 0;
63 
64           for (;;)
65           {
66                     while (LDAP_SPACE(*p))                   /* Skip leading whitespace */
67                               p++;
68 
69                     if (*p == '\0')                         /* End of string? */
70                               return count;
71 
72                     count++;                                /* Found start of a key */
73 
74                     while (!LDAP_SPACE(*p))       /* Skip till next space or end of string. */
75                               if (*p++ == '\0')
76                                         return count;
77           }
78 }
79 
80 
81 /* ---------------------------------------------------------------------------
82    readNextKey
83 
84    Internal function to parse the next sort key in the string.
85    Allocate an LDAPSortKey structure and initialize it with
86    attribute name, reverse flag, and matching rule OID.
87 
88    Each sort key in the string has the format:
89             [whitespace][-]attribute[:[OID]]
90 
91    pNextKey    (IN/OUT) Points to the next key in the sortkey string to parse.
92                                                             The pointer is updated to point to the next character
93                                                             after the sortkey being parsed.
94 
95    key         (OUT)    Points to the address of an LDAPSortKey structure
96                                                             which has been allocated by this routine and
97                                                             initialized with information from the next sortkey.
98    ---------------------------------------------------------------------------*/
99 
readNextKey(char ** pNextKey,LDAPSortKey ** key)100 static int readNextKey( char **pNextKey, LDAPSortKey **key)
101 {
102           char *p = *pNextKey;
103           int rev = 0;
104           char *attrStart;
105           int attrLen;
106           char *oidStart = NULL;
107           int oidLen = 0;
108 
109           /* Skip leading white space. */
110           while (LDAP_SPACE(*p))
111                     p++;
112 
113           if (*p == '-')                 /* Check if the reverse flag is present. */
114           {
115                     rev=1;
116                     p++;
117           }
118 
119           /* We're now positioned at the start of the attribute. */
120           attrStart = p;
121 
122           /* Get the length of the attribute until the next whitespace or ":". */
123           attrLen = strcspn(p, " \t:");
124           p += attrLen;
125 
126           if (attrLen == 0)    /* If no attribute name was present, quit. */
127                     return LDAP_PARAM_ERROR;
128 
129           if (*p == ':')
130           {
131                     oidStart = ++p;                                    /* Start of the OID, after the colon */
132                     oidLen = strcspn(p, " \t");    /* Get length of OID till next whitespace */
133                     p += oidLen;
134           }
135 
136           *pNextKey = p;                 /* Update argument to point to next key */
137 
138           /* Allocate an LDAPSortKey structure */
139           *key = LDAP_MALLOC(sizeof(LDAPSortKey));
140           if (*key == NULL) return LDAP_NO_MEMORY;
141 
142           /* Allocate memory for the attribute and copy to it. */
143           (*key)->attributeType = LDAP_MALLOC(attrLen+1);
144           if ((*key)->attributeType == NULL) {
145                     LDAP_FREE(*key);
146                     return LDAP_NO_MEMORY;
147           }
148 
149           strncpy((*key)->attributeType, attrStart, attrLen);
150           (*key)->attributeType[attrLen] = 0;
151 
152           /* If present, allocate memory for the OID and copy to it. */
153           if (oidLen) {
154                     (*key)->orderingRule = LDAP_MALLOC(oidLen+1);
155                     if ((*key)->orderingRule == NULL) {
156                               LDAP_FREE((*key)->attributeType);
157                               LDAP_FREE(*key);
158                               return LDAP_NO_MEMORY;
159                     }
160                     strncpy((*key)->orderingRule, oidStart, oidLen);
161                     (*key)->orderingRule[oidLen] = 0;
162 
163           } else {
164                     (*key)->orderingRule = NULL;
165           }
166 
167           (*key)->reverseOrder = rev;
168 
169           return LDAP_SUCCESS;
170 }
171 
172 
173 /* ---------------------------------------------------------------------------
174    ldap_create_sort_keylist
175 
176    Create an array of pointers to LDAPSortKey structures, containing the
177    information specified by the string representation of one or more
178    sort keys.
179 
180    sortKeyList    (OUT) Points to a null-terminated array of pointers to
181                                                             LDAPSortKey structures allocated by this routine.
182                                                             This memory SHOULD be freed by the calling program
183                                                             using ldap_free_sort_keylist().
184 
185    keyString      (IN)  Points to a string of one or more sort keys.
186 
187    ---------------------------------------------------------------------------*/
188 
189 int
ldap_create_sort_keylist(LDAPSortKey *** sortKeyList,char * keyString)190 ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
191 {
192           int         numKeys, rc, i;
193           char        *nextKey;
194           LDAPSortKey **keyList = NULL;
195 
196           assert( sortKeyList != NULL );
197           assert( keyString != NULL );
198 
199           *sortKeyList = NULL;
200 
201           /* Determine the number of sort keys so we can allocate memory. */
202           if (( numKeys = countKeys(keyString)) == 0) {
203                     return LDAP_PARAM_ERROR;
204           }
205 
206           /* Allocate the array of pointers.  Initialize to NULL. */
207           keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
208           if ( keyList == NULL) return LDAP_NO_MEMORY;
209 
210           /* For each sort key in the string, create an LDAPSortKey structure
211              and add it to the list.
212           */
213           nextKey = keyString;                      /* Points to the next key in the string */
214           for (i=0; i < numKeys; i++) {
215                     rc = readNextKey(&nextKey, &keyList[i]);
216 
217                     if (rc != LDAP_SUCCESS) {
218                               ldap_free_sort_keylist(keyList);
219                               return rc;
220                     }
221           }
222 
223           *sortKeyList = keyList;
224           return LDAP_SUCCESS;
225 }
226 
227 
228 /* ---------------------------------------------------------------------------
229    ldap_free_sort_keylist
230 
231    Frees the sort key structures created by ldap_create_sort_keylist().
232    Frees the memory referenced by the LDAPSortKey structures,
233    the LDAPSortKey structures themselves, and the array of pointers
234    to the structures.
235 
236    keyList     (IN) Points to an array of pointers to LDAPSortKey structures.
237    ---------------------------------------------------------------------------*/
238 
239 void
ldap_free_sort_keylist(LDAPSortKey ** keyList)240 ldap_free_sort_keylist ( LDAPSortKey **keyList )
241 {
242           int i;
243           LDAPSortKey *nextKeyp;
244 
245           if (keyList == NULL) return;
246 
247           i=0;
248           while ( 0 != (nextKeyp = keyList[i++]) ) {
249                     if (nextKeyp->attributeType) {
250                               LBER_FREE(nextKeyp->attributeType);
251                     }
252 
253                     if (nextKeyp->orderingRule != NULL) {
254                               LBER_FREE(nextKeyp->orderingRule);
255                     }
256 
257                     LBER_FREE(nextKeyp);
258           }
259 
260           LBER_FREE(keyList);
261 }
262 
263 
264 /* ---------------------------------------------------------------------------
265    ldap_create_sort_control_value
266 
267    Create and encode the value of the server-side sort control.
268 
269    ld          (IN) An LDAP session handle, as obtained from a call to
270                                                   ldap_init().
271 
272    keyList     (IN) Points to a null-terminated array of pointers to
273                                                   LDAPSortKey structures, containing a description of
274                                                   each of the sort keys to be used.  The description
275                                                   consists of an attribute name, ascending/descending flag,
276                                                   and an optional matching rule (OID) to use.
277 
278    value      (OUT) Contains the control value; the bv_val member of the berval structure
279                                                   SHOULD be freed by calling ldap_memfree() when done.
280 
281 
282    Ber encoding
283 
284    SortKeyList ::= SEQUENCE OF SEQUENCE {
285                        attributeType   AttributeDescription,
286                        orderingRule    [0] MatchingRuleId OPTIONAL,
287                        reverseOrder    [1] BOOLEAN DEFAULT FALSE }
288 
289    ---------------------------------------------------------------------------*/
290 
291 int
ldap_create_sort_control_value(LDAP * ld,LDAPSortKey ** keyList,struct berval * value)292 ldap_create_sort_control_value(
293           LDAP *ld,
294           LDAPSortKey **keyList,
295           struct berval *value )
296 {
297           int                 i;
298           BerElement          *ber = NULL;
299           ber_tag_t tag;
300 
301           assert( ld != NULL );
302           assert( LDAP_VALID( ld ) );
303 
304           if ( ld == NULL ) return LDAP_PARAM_ERROR;
305           if ( keyList == NULL || value == NULL ) {
306                     ld->ld_errno = LDAP_PARAM_ERROR;
307                     return LDAP_PARAM_ERROR;
308           }
309 
310           value->bv_val = NULL;
311           value->bv_len = 0;
312           ld->ld_errno = LDAP_SUCCESS;
313 
314           ber = ldap_alloc_ber_with_options( ld );
315           if ( ber == NULL) {
316                     ld->ld_errno = LDAP_NO_MEMORY;
317                     return ld->ld_errno;
318           }
319 
320           tag = ber_printf( ber, "{" /*}*/ );
321           if ( tag == LBER_ERROR ) {
322                     goto error_return;
323           }
324 
325           for ( i = 0; keyList[i] != NULL; i++ ) {
326                     tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );
327                     if ( tag == LBER_ERROR ) {
328                               goto error_return;
329                     }
330 
331                     if ( keyList[i]->orderingRule != NULL ) {
332                               tag = ber_printf( ber, "ts",
333                                         LDAP_MATCHRULE_IDENTIFIER,
334                                         keyList[i]->orderingRule );
335 
336                               if ( tag == LBER_ERROR ) {
337                                         goto error_return;
338                               }
339                     }
340 
341                     if ( keyList[i]->reverseOrder ) {
342                               tag = ber_printf( ber, "tb",
343                                         LDAP_REVERSEORDER_IDENTIFIER,
344                                         keyList[i]->reverseOrder );
345 
346                               if ( tag == LBER_ERROR ) {
347                                         goto error_return;
348                               }
349                     }
350 
351                     tag = ber_printf( ber, /*{*/ "N}" );
352                     if ( tag == LBER_ERROR ) {
353                               goto error_return;
354                     }
355           }
356 
357           tag = ber_printf( ber, /*{*/ "N}" );
358           if ( tag == LBER_ERROR ) {
359                     goto error_return;
360           }
361 
362           if ( ber_flatten2( ber, value, 1 ) == -1 ) {
363                     ld->ld_errno = LDAP_NO_MEMORY;
364           }
365 
366           if ( 0 ) {
367 error_return:;
368                     ld->ld_errno =  LDAP_ENCODING_ERROR;
369           }
370 
371           if ( ber != NULL ) {
372                     ber_free( ber, 1 );
373           }
374 
375           return ld->ld_errno;
376 }
377 
378 
379 /* ---------------------------------------------------------------------------
380    ldap_create_sort_control
381 
382    Create and encode the server-side sort control.
383 
384    ld          (IN) An LDAP session handle, as obtained from a call to
385                                                   ldap_init().
386 
387    keyList     (IN) Points to a null-terminated array of pointers to
388                                                   LDAPSortKey structures, containing a description of
389                                                   each of the sort keys to be used.  The description
390                                                   consists of an attribute name, ascending/descending flag,
391                                                   and an optional matching rule (OID) to use.
392 
393    isCritical  (IN) 0 - Indicates the control is not critical to the operation.
394                                                   non-zero - The control is critical to the operation.
395 
396    ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
397                                                   SHOULD be freed by calling ldap_control_free() when done.
398 
399 
400    Ber encoding
401 
402    SortKeyList ::= SEQUENCE OF SEQUENCE {
403                        attributeType   AttributeDescription,
404                        orderingRule    [0] MatchingRuleId OPTIONAL,
405                        reverseOrder    [1] BOOLEAN DEFAULT FALSE }
406 
407    ---------------------------------------------------------------------------*/
408 
409 int
ldap_create_sort_control(LDAP * ld,LDAPSortKey ** keyList,int isCritical,LDAPControl ** ctrlp)410 ldap_create_sort_control(
411           LDAP *ld,
412           LDAPSortKey **keyList,
413           int isCritical,
414           LDAPControl **ctrlp )
415 {
416           struct berval       value;
417 
418           assert( ld != NULL );
419           assert( LDAP_VALID( ld ) );
420 
421           if ( ld == NULL ) {
422                     return LDAP_PARAM_ERROR;
423           }
424 
425           if ( ctrlp == NULL ) {
426                     ld->ld_errno = LDAP_PARAM_ERROR;
427                     return ld->ld_errno;
428           }
429 
430           ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
431           if ( ld->ld_errno == LDAP_SUCCESS ) {
432                     ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,
433                               isCritical, &value, 0, ctrlp );
434                     if ( ld->ld_errno != LDAP_SUCCESS ) {
435                               LDAP_FREE( value.bv_val );
436                     }
437           }
438 
439           return ld->ld_errno;
440 }
441 
442 
443 /* ---------------------------------------------------------------------------
444    ldap_parse_sortedresult_control
445 
446    Decode the server-side sort control return information.
447 
448    ld          (IN) An LDAP session handle, as obtained from a call to
449                                                   ldap_init().
450 
451    ctrl        (IN) The address of the LDAP Control Structure.
452 
453    returnCode (OUT) This result parameter is filled in with the sort control
454                                                   result code.  This parameter MUST not be NULL.
455 
456    attribute  (OUT) If an error occurred the server may return a string
457                                                   indicating the first attribute in the sortkey list
458                                                   that was in error.  If a string is returned, the memory
459                                                   should be freed with ldap_memfree.  If this parameter is
460                                                   NULL, no string is returned.
461 
462 
463    Ber encoding for sort control
464 
465            SortResult ::= SEQUENCE {
466                     sortResult  ENUMERATED {
467                               success                   (0), -- results are sorted
468                               operationsError           (1), -- server internal failure
469                               timeLimitExceeded         (3), -- timelimit reached before
470                                                                                                        -- sorting was completed
471                               strongAuthRequired        (8), -- refused to return sorted
472                                                                                                        -- results via insecure
473                                                                                                        -- protocol
474                               adminLimitExceeded       (11), -- too many matching entries
475                                                                                                        -- for the server to sort
476                               noSuchAttribute          (16), -- unrecognized attribute
477                                                                                                        -- type in sort key
478                               inappropriateMatching    (18), -- unrecognized or inappro-
479                                                                                                        -- priate matching rule in
480                                                                                                        -- sort key
481                               insufficientAccessRights (50), -- refused to return sorted
482                                                                                                        -- results to this client
483                               busy                     (51), -- too busy to process
484                               unwillingToPerform       (53), -- unable to sort
485                               other                    (80)
486                               },
487             attributeType [0] AttributeDescription OPTIONAL }
488    ---------------------------------------------------------------------------*/
489 
490 int
ldap_parse_sortresponse_control(LDAP * ld,LDAPControl * ctrl,ber_int_t * returnCode,char ** attribute)491 ldap_parse_sortresponse_control(
492           LDAP *ld,
493           LDAPControl *ctrl,
494           ber_int_t *returnCode,
495           char **attribute )
496 {
497           BerElement *ber;
498           ber_tag_t tag, berTag;
499           ber_len_t berLen;
500 
501           assert( ld != NULL );
502           assert( LDAP_VALID( ld ) );
503 
504           if (ld == NULL) {
505                     return LDAP_PARAM_ERROR;
506           }
507 
508           if (ctrl == NULL) {
509                     ld->ld_errno =  LDAP_PARAM_ERROR;
510                     return(ld->ld_errno);
511           }
512 
513           if (attribute) {
514                     *attribute = NULL;
515           }
516 
517           if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {
518                     /* Not sort result control */
519                     ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
520                     return(ld->ld_errno);
521           }
522 
523           /* Create a BerElement from the berval returned in the control. */
524           ber = ber_init(&ctrl->ldctl_value);
525 
526           if (ber == NULL) {
527                     ld->ld_errno = LDAP_NO_MEMORY;
528                     return(ld->ld_errno);
529           }
530 
531           /* Extract the result code from the control. */
532           tag = ber_scanf(ber, "{e" /*}*/, returnCode);
533 
534           if( tag == LBER_ERROR ) {
535                     ber_free(ber, 1);
536                     ld->ld_errno = LDAP_DECODING_ERROR;
537                     return(ld->ld_errno);
538           }
539 
540           /* If caller wants the attribute name, and if it's present in the control,
541              extract the attribute name which caused the error. */
542           if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
543           {
544                     tag = ber_scanf(ber, "ta", &berTag, attribute);
545 
546                     if (tag == LBER_ERROR ) {
547                               ber_free(ber, 1);
548                               ld->ld_errno = LDAP_DECODING_ERROR;
549                               return(ld->ld_errno);
550                     }
551           }
552 
553           ber_free(ber,1);
554 
555           ld->ld_errno = LDAP_SUCCESS;
556           return(ld->ld_errno);
557 }
558