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