Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: sortctrl.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 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.4 2025/09/05 21:16:21 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 
     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 
    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
    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
    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
    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
    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
    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