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