/* $OpenLDAP$ */1/* This work is part of OpenLDAP Software <http://www.openldap.org/>.2*3* Copyright 1998-2024 The OpenLDAP Foundation.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted only as authorized by the OpenLDAP8* Public License.9*10* A copy of this license is available in the file LICENSE in the11* top-level directory of the distribution or, alternatively, at12* <http://www.OpenLDAP.org/license.html>.13*/14/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.15*16* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND17* TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT18* TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS19* AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"20* IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION21* OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP22* PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT23* THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.24*/25/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License26* can be found in the file "build/LICENSE-2.0.1" in this distribution27* of OpenLDAP Software.28*/2930#include "portable.h"3132#include <stdio.h>33#include <ac/stdlib.h>34#include <ac/string.h>35#include <ac/time.h>3637#include "ldap-int.h"3839#define LDAP_MATCHRULE_IDENTIFIER 0x80L40#define LDAP_REVERSEORDER_IDENTIFIER 0x81L41#define LDAP_ATTRTYPES_IDENTIFIER 0x80L42434445/* ---------------------------------------------------------------------------46countKeys4748Internal function to determine the number of keys in the string.4950keyString (IN) String of items separated by whitespace.51---------------------------------------------------------------------------*/5253static int countKeys(char *keyString)54{55char *p = keyString;56int count = 0;5758for (;;)59{60while (LDAP_SPACE(*p)) /* Skip leading whitespace */61p++;6263if (*p == '\0') /* End of string? */64return count;6566count++; /* Found start of a key */6768while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */69if (*p++ == '\0')70return count;71}72}737475/* ---------------------------------------------------------------------------76readNextKey7778Internal function to parse the next sort key in the string.79Allocate an LDAPSortKey structure and initialize it with80attribute name, reverse flag, and matching rule OID.8182Each sort key in the string has the format:83[whitespace][-]attribute[:[OID]]8485pNextKey (IN/OUT) Points to the next key in the sortkey string to parse.86The pointer is updated to point to the next character87after the sortkey being parsed.8889key (OUT) Points to the address of an LDAPSortKey structure90which has been allocated by this routine and91initialized with information from the next sortkey.92---------------------------------------------------------------------------*/9394static int readNextKey( char **pNextKey, LDAPSortKey **key)95{96char *p = *pNextKey;97int rev = 0;98char *attrStart;99int attrLen;100char *oidStart = NULL;101int oidLen = 0;102103/* Skip leading white space. */104while (LDAP_SPACE(*p))105p++;106107if (*p == '-') /* Check if the reverse flag is present. */108{109rev=1;110p++;111}112113/* We're now positioned at the start of the attribute. */114attrStart = p;115116/* Get the length of the attribute until the next whitespace or ":". */117attrLen = strcspn(p, " \t:");118p += attrLen;119120if (attrLen == 0) /* If no attribute name was present, quit. */121return LDAP_PARAM_ERROR;122123if (*p == ':')124{125oidStart = ++p; /* Start of the OID, after the colon */126oidLen = strcspn(p, " \t"); /* Get length of OID till next whitespace */127p += oidLen;128}129130*pNextKey = p; /* Update argument to point to next key */131132/* Allocate an LDAPSortKey structure */133*key = LDAP_MALLOC(sizeof(LDAPSortKey));134if (*key == NULL) return LDAP_NO_MEMORY;135136/* Allocate memory for the attribute and copy to it. */137(*key)->attributeType = LDAP_MALLOC(attrLen+1);138if ((*key)->attributeType == NULL) {139LDAP_FREE(*key);140return LDAP_NO_MEMORY;141}142143strncpy((*key)->attributeType, attrStart, attrLen);144(*key)->attributeType[attrLen] = 0;145146/* If present, allocate memory for the OID and copy to it. */147if (oidLen) {148(*key)->orderingRule = LDAP_MALLOC(oidLen+1);149if ((*key)->orderingRule == NULL) {150LDAP_FREE((*key)->attributeType);151LDAP_FREE(*key);152return LDAP_NO_MEMORY;153}154strncpy((*key)->orderingRule, oidStart, oidLen);155(*key)->orderingRule[oidLen] = 0;156157} else {158(*key)->orderingRule = NULL;159}160161(*key)->reverseOrder = rev;162163return LDAP_SUCCESS;164}165166167/* ---------------------------------------------------------------------------168ldap_create_sort_keylist169170Create an array of pointers to LDAPSortKey structures, containing the171information specified by the string representation of one or more172sort keys.173174sortKeyList (OUT) Points to a null-terminated array of pointers to175LDAPSortKey structures allocated by this routine.176This memory SHOULD be freed by the calling program177using ldap_free_sort_keylist().178179keyString (IN) Points to a string of one or more sort keys.180181---------------------------------------------------------------------------*/182183int184ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )185{186int numKeys, rc, i;187char *nextKey;188LDAPSortKey **keyList = NULL;189190assert( sortKeyList != NULL );191assert( keyString != NULL );192193*sortKeyList = NULL;194195/* Determine the number of sort keys so we can allocate memory. */196if (( numKeys = countKeys(keyString)) == 0) {197return LDAP_PARAM_ERROR;198}199200/* Allocate the array of pointers. Initialize to NULL. */201keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));202if ( keyList == NULL) return LDAP_NO_MEMORY;203204/* For each sort key in the string, create an LDAPSortKey structure205and add it to the list.206*/207nextKey = keyString; /* Points to the next key in the string */208for (i=0; i < numKeys; i++) {209rc = readNextKey(&nextKey, &keyList[i]);210211if (rc != LDAP_SUCCESS) {212ldap_free_sort_keylist(keyList);213return rc;214}215}216217*sortKeyList = keyList;218return LDAP_SUCCESS;219}220221222/* ---------------------------------------------------------------------------223ldap_free_sort_keylist224225Frees the sort key structures created by ldap_create_sort_keylist().226Frees the memory referenced by the LDAPSortKey structures,227the LDAPSortKey structures themselves, and the array of pointers228to the structures.229230keyList (IN) Points to an array of pointers to LDAPSortKey structures.231---------------------------------------------------------------------------*/232233void234ldap_free_sort_keylist ( LDAPSortKey **keyList )235{236int i;237LDAPSortKey *nextKeyp;238239if (keyList == NULL) return;240241i=0;242while ( 0 != (nextKeyp = keyList[i++]) ) {243if (nextKeyp->attributeType) {244LBER_FREE(nextKeyp->attributeType);245}246247if (nextKeyp->orderingRule != NULL) {248LBER_FREE(nextKeyp->orderingRule);249}250251LBER_FREE(nextKeyp);252}253254LBER_FREE(keyList);255}256257258/* ---------------------------------------------------------------------------259ldap_create_sort_control_value260261Create and encode the value of the server-side sort control.262263ld (IN) An LDAP session handle, as obtained from a call to264ldap_init().265266keyList (IN) Points to a null-terminated array of pointers to267LDAPSortKey structures, containing a description of268each of the sort keys to be used. The description269consists of an attribute name, ascending/descending flag,270and an optional matching rule (OID) to use.271272value (OUT) Contains the control value; the bv_val member of the berval structure273SHOULD be freed by calling ldap_memfree() when done.274275276Ber encoding277278SortKeyList ::= SEQUENCE OF SEQUENCE {279attributeType AttributeDescription,280orderingRule [0] MatchingRuleId OPTIONAL,281reverseOrder [1] BOOLEAN DEFAULT FALSE }282283---------------------------------------------------------------------------*/284285int286ldap_create_sort_control_value(287LDAP *ld,288LDAPSortKey **keyList,289struct berval *value )290{291int i;292BerElement *ber = NULL;293ber_tag_t tag;294295assert( ld != NULL );296assert( LDAP_VALID( ld ) );297298if ( ld == NULL ) return LDAP_PARAM_ERROR;299if ( keyList == NULL || value == NULL ) {300ld->ld_errno = LDAP_PARAM_ERROR;301return LDAP_PARAM_ERROR;302}303304value->bv_val = NULL;305value->bv_len = 0;306ld->ld_errno = LDAP_SUCCESS;307308ber = ldap_alloc_ber_with_options( ld );309if ( ber == NULL) {310ld->ld_errno = LDAP_NO_MEMORY;311return ld->ld_errno;312}313314tag = ber_printf( ber, "{" /*}*/ );315if ( tag == LBER_ERROR ) {316goto error_return;317}318319for ( i = 0; keyList[i] != NULL; i++ ) {320tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );321if ( tag == LBER_ERROR ) {322goto error_return;323}324325if ( keyList[i]->orderingRule != NULL ) {326tag = ber_printf( ber, "ts",327LDAP_MATCHRULE_IDENTIFIER,328keyList[i]->orderingRule );329330if ( tag == LBER_ERROR ) {331goto error_return;332}333}334335if ( keyList[i]->reverseOrder ) {336tag = ber_printf( ber, "tb",337LDAP_REVERSEORDER_IDENTIFIER,338keyList[i]->reverseOrder );339340if ( tag == LBER_ERROR ) {341goto error_return;342}343}344345tag = ber_printf( ber, /*{*/ "N}" );346if ( tag == LBER_ERROR ) {347goto error_return;348}349}350351tag = ber_printf( ber, /*{*/ "N}" );352if ( tag == LBER_ERROR ) {353goto error_return;354}355356if ( ber_flatten2( ber, value, 1 ) == -1 ) {357ld->ld_errno = LDAP_NO_MEMORY;358}359360if ( 0 ) {361error_return:;362ld->ld_errno = LDAP_ENCODING_ERROR;363}364365if ( ber != NULL ) {366ber_free( ber, 1 );367}368369return ld->ld_errno;370}371372373/* ---------------------------------------------------------------------------374ldap_create_sort_control375376Create and encode the server-side sort control.377378ld (IN) An LDAP session handle, as obtained from a call to379ldap_init().380381keyList (IN) Points to a null-terminated array of pointers to382LDAPSortKey structures, containing a description of383each of the sort keys to be used. The description384consists of an attribute name, ascending/descending flag,385and an optional matching rule (OID) to use.386387isCritical (IN) 0 - Indicates the control is not critical to the operation.388non-zero - The control is critical to the operation.389390ctrlp (OUT) Returns a pointer to the LDAPControl created. This control391SHOULD be freed by calling ldap_control_free() when done.392393394Ber encoding395396SortKeyList ::= SEQUENCE OF SEQUENCE {397attributeType AttributeDescription,398orderingRule [0] MatchingRuleId OPTIONAL,399reverseOrder [1] BOOLEAN DEFAULT FALSE }400401---------------------------------------------------------------------------*/402403int404ldap_create_sort_control(405LDAP *ld,406LDAPSortKey **keyList,407int isCritical,408LDAPControl **ctrlp )409{410struct berval value;411412assert( ld != NULL );413assert( LDAP_VALID( ld ) );414415if ( ld == NULL ) {416return LDAP_PARAM_ERROR;417}418419if ( ctrlp == NULL ) {420ld->ld_errno = LDAP_PARAM_ERROR;421return ld->ld_errno;422}423424ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );425if ( ld->ld_errno == LDAP_SUCCESS ) {426ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,427isCritical, &value, 0, ctrlp );428if ( ld->ld_errno != LDAP_SUCCESS ) {429LDAP_FREE( value.bv_val );430}431}432433return ld->ld_errno;434}435436437/* ---------------------------------------------------------------------------438ldap_parse_sortedresult_control439440Decode the server-side sort control return information.441442ld (IN) An LDAP session handle, as obtained from a call to443ldap_init().444445ctrl (IN) The address of the LDAP Control Structure.446447returnCode (OUT) This result parameter is filled in with the sort control448result code. This parameter MUST not be NULL.449450attribute (OUT) If an error occurred the server may return a string451indicating the first attribute in the sortkey list452that was in error. If a string is returned, the memory453should be freed with ldap_memfree. If this parameter is454NULL, no string is returned.455456457Ber encoding for sort control458459SortResult ::= SEQUENCE {460sortResult ENUMERATED {461success (0), -- results are sorted462operationsError (1), -- server internal failure463timeLimitExceeded (3), -- timelimit reached before464-- sorting was completed465strongAuthRequired (8), -- refused to return sorted466-- results via insecure467-- protocol468adminLimitExceeded (11), -- too many matching entries469-- for the server to sort470noSuchAttribute (16), -- unrecognized attribute471-- type in sort key472inappropriateMatching (18), -- unrecognized or inappro-473-- priate matching rule in474-- sort key475insufficientAccessRights (50), -- refused to return sorted476-- results to this client477busy (51), -- too busy to process478unwillingToPerform (53), -- unable to sort479other (80)480},481attributeType [0] AttributeDescription OPTIONAL }482---------------------------------------------------------------------------*/483484int485ldap_parse_sortresponse_control(486LDAP *ld,487LDAPControl *ctrl,488ber_int_t *returnCode,489char **attribute )490{491BerElement *ber;492ber_tag_t tag, berTag;493ber_len_t berLen;494495assert( ld != NULL );496assert( LDAP_VALID( ld ) );497498if (ld == NULL) {499return LDAP_PARAM_ERROR;500}501502if (ctrl == NULL) {503ld->ld_errno = LDAP_PARAM_ERROR;504return(ld->ld_errno);505}506507if (attribute) {508*attribute = NULL;509}510511if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {512/* Not sort result control */513ld->ld_errno = LDAP_CONTROL_NOT_FOUND;514return(ld->ld_errno);515}516517/* Create a BerElement from the berval returned in the control. */518ber = ber_init(&ctrl->ldctl_value);519520if (ber == NULL) {521ld->ld_errno = LDAP_NO_MEMORY;522return(ld->ld_errno);523}524525/* Extract the result code from the control. */526tag = ber_scanf(ber, "{e" /*}*/, returnCode);527528if( tag == LBER_ERROR ) {529ber_free(ber, 1);530ld->ld_errno = LDAP_DECODING_ERROR;531return(ld->ld_errno);532}533534/* If caller wants the attribute name, and if it's present in the control,535extract the attribute name which caused the error. */536if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))537{538tag = ber_scanf(ber, "ta", &berTag, attribute);539540if (tag == LBER_ERROR ) {541ber_free(ber, 1);542ld->ld_errno = LDAP_DECODING_ERROR;543return(ld->ld_errno);544}545}546547ber_free(ber,1);548549ld->ld_errno = LDAP_SUCCESS;550return(ld->ld_errno);551}552553554