xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/sortctrl.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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