1 /* $NetBSD: sortctrl.c,v 1.2 2020/08/11 13:15:37 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2020 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.2 2020/08/11 13:15:37 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 stucture 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 occured 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